<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>wylu</title>
  
  <subtitle>Keep It Simple, Stupid</subtitle>
  <link href="https://www.wylu.me/atom.xml" rel="self"/>
  
  <link href="https://www.wylu.me/"/>
  <updated>2023-08-08T13:41:18.951Z</updated>
  <id>https://www.wylu.me/</id>
  
  <author>
    <name>lu wenye</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>FastDFS 安装部署</title>
    <link href="https://www.wylu.me/posts/4d3c18db/"/>
    <id>https://www.wylu.me/posts/4d3c18db/</id>
    <published>2023-08-08T13:31:59.000Z</published>
    <updated>2023-08-08T13:41:18.951Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了 FastDFS 的安装部署过程。首先，需要准备好设备环境，并安装相关依赖。接着，按照以下步骤进行安装：下载 FastDFS，解压安装，编辑配置文件，创建 system 服务，启动服务，并查看状态。最后，进行 Client 测试，测试上传文件，返回 ID 表示成功。</p><span id="more"></span><h1 id="fastdfs-安装部署">FastDFS 安装部署</h1><h2 id="设备环境">1. 设备环境</h2><table><thead><tr class="header"><th>Host</th><th>IP</th><th>Port</th><th>OS</th><th>Software</th></tr></thead><tbody><tr class="odd"><td>cnode1</td><td>10.128.170.30</td><td>22122</td><td>CentOS 7.9.2009</td><td>fastfds 6.06 trackerd</td></tr><tr class="even"><td>cnode1</td><td>10.128.170.30</td><td>23000</td><td>CentOS 7.9.2009</td><td>fastfds 6.06 storaged</td></tr><tr class="odd"><td>cnode2</td><td>10.128.170.31</td><td>22122</td><td>CentOS 7.9.2009</td><td>fastfds 6.06 trackerd</td></tr><tr class="even"><td>cnode2</td><td>10.128.170.31</td><td>23000</td><td>CentOS 7.9.2009</td><td>fastfds 6.06 storaged</td></tr></tbody></table><p>编辑 hosts 文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/hosts</span><br></pre></td></tr></table></figure><p>添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">10.128.170.30 cnode1</span><br><span class="line">10.128.170.31 cnode2</span><br></pre></td></tr></table></figure><h2 id="安装依赖">2. 安装依赖</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">yum -y install gcc gcc+ gcc-c++ openssl openssl-devel pcre pcre-deve</span><br><span class="line">yum -y install pcre-devel openssl openssl-develyum -y install zlib zlib-deve</span><br><span class="line">yum -y install perl</span><br><span class="line">yum -y install unzip</span><br></pre></td></tr></table></figure><h2 id="安装步骤">3. 安装步骤</h2><h3 id="下载-fastfds">3.1 下载 fastfds</h3><p><a href="https://github.com/happyfish100/fastdfs/wiki/">fastdfs wiki</a></p><p>根据 wiki 下载以下软件包：</p><ul><li>libfastcommon-master.zip</li><li>fastdfs-6.06.tar.gz</li></ul><h3 id="解压安装">3.2 解压安装</h3><p>创建安装目录和数据目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkdir /opt/fastdfs</span><br><span class="line">mkdir -p /data/fastdfs/&#123;fastdfs_tracker,fastdfs_storage,fastdfs_storage_data1,fastdfs_storage_data2&#125;</span><br></pre></td></tr></table></figure><p>解压</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">unzip -d /opt/fastdfs/ /saasdata/soft/libfastcommon-master.zip</span><br><span class="line">tar -zxvf /saasdata/soft/fastdfs-6.06.tar.gz -C /opt/fastdfs/</span><br></pre></td></tr></table></figure><p>修改安装路径</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sed -i &#x27;3c TARGET_PREFIX=/opt/fastdfs/fastdfs-6.06&#x27; /opt/fastdfs/fastdfs-6.06/make.sh</span><br><span class="line">sed -i &#x27;4c TARGET_CONF_PATH=/opt/fastdfs/fastdfs-6.06/conf&#x27; /opt/fastdfs/fastdfs-6.06/make.sh</span><br><span class="line">sed -i &#x27;5c TARGET_INIT_PATH=/opt/fastdfs/fastdfs-6.06/init.d&#x27; /opt/fastdfs/fastdfs-6.06/make.sh</span><br></pre></td></tr></table></figure><p>编译安装</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /opt/fastdfs/libfastcommon-master &amp;&amp; ./make.sh &amp;&amp; ./make.sh install &gt; /dev/null</span><br><span class="line">cd /opt/fastdfs/fastdfs-6.06 &amp;&amp; ./make.sh &amp;&amp; ./make.sh install &gt; /dev/null</span><br></pre></td></tr></table></figure><p><strong>在 cnode1，cnode2 上进行同样的操作。</strong></p><h3 id="编辑配置文件">3.3 编辑配置文件</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">sed -i  &#x27;23c  base_path=/data/fastdfs/fastdfs_tracker&#x27; /opt/fastdfs/fastdfs-6.06/conf/tracker.conf</span><br><span class="line">sed -i  &#x27;11c  group_name=group1&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;24c  port = 23000&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;49c  base_path = /data/fastdfs/fastdfs_storage&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;119c  store_path_count = 2&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;129c  store_path0 = /data/fastdfs/fastdfs_storage_data1&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;130c  store_path1 = /data/fastdfs/fastdfs_storage_data2&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;145c  tracker_server = cnode1:22122&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;146c  tracker_server = cnode2:22122&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;180c  file_distribute_path_mode = 0&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br><span class="line">sed -i  &#x27;352c  http.server_port = 8888&#x27; /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br></pre></td></tr></table></figure><p><strong>在 cnode1，cnode2 上进行同样的操作。</strong></p><h3 id="创建-system-服务">3.4 创建 system 服务</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">cat &gt; /usr/lib/systemd/system/fdfs_trackerd.service &lt;&lt; EOF</span><br><span class="line">[Unit]</span><br><span class="line">Description=fdfs_trackerd Server</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=forking</span><br><span class="line">ExecStart=/opt/fastdfs/fastdfs-6.06/bin/fdfs_trackerd /opt/fastdfs/fastdfs-6.06/conf/tracker.conf start</span><br><span class="line">ExecStop=/opt/fastdfs/fastdfs-6.06/bin/fdfs_trackerd /opt/fastdfs/fastdfs-6.06/conf/tracker.conf stop</span><br><span class="line">PrivateTmp=true</span><br><span class="line">Restart=always</span><br><span class="line">RestartSec=1</span><br><span class="line">StartLimitInterval=0</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">cat &gt; /usr/lib/systemd/system/fdfs_storaged.service &lt;&lt; EOF</span><br><span class="line">[Unit]</span><br><span class="line">Description=fdfs_storaged</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=forking</span><br><span class="line">ExecStart=/opt/fastdfs/fastdfs-6.06/bin/fdfs_storaged /opt/fastdfs/fastdfs-6.06/conf/storage.conf start</span><br><span class="line">ExecStop=/opt/fastdfs/fastdfs-6.06/bin/fdfs_storaged /opt/fastdfs/fastdfs-6.06/conf/storage.conf stop</span><br><span class="line">PrivateTmp=true</span><br><span class="line">Restart=always</span><br><span class="line">RestartSec=1</span><br><span class="line">StartLimitInterval=0</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p><strong>在 cnode1，cnode2 上进行同样的操作。</strong></p><h3 id="启动服务">3.5 启动服务</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl start fdfs_trackerd.service</span><br><span class="line">systemctl start fdfs_storaged.service</span><br></pre></td></tr></table></figure><p><strong>在 cnode1，cnode2 上进行同样的操作。</strong></p><p>查看状态</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/opt/fastdfs/fastdfs-6.06/bin/fdfs_monitor /opt/fastdfs/fastdfs-6.06/conf/storage.conf</span><br></pre></td></tr></table></figure><h2 id="client-测试">4. Client 测试</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /opt/fastdfs/fastdfs-6.06/conf/client.conf</span><br></pre></td></tr></table></figure><p>修改如下配置</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">base_path</span> = /opt/fastdfs/fastdfs-<span class="number">6.06</span></span><br><span class="line"><span class="attr">tracker_server</span> = cnode1:<span class="number">22122</span></span><br><span class="line"><span class="attr">tracker_server</span> = cnode2:<span class="number">22122</span></span><br></pre></td></tr></table></figure><p>测试上传文件，返回 ID 表示成功，如：group1/M00/00/00/xx.tar.gz</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 bin]# ./fdfs_upload_file /opt/fastdfs/fastdfs-6.06/conf/client.conf /root/Downloads/fastdfs-6.06.tar.gz</span><br><span class="line">group1/M00/00/00/CoCqFWIUhZeATgjaAAxZcPR00vw.tar.gz</span><br><span class="line">[root@cnode1 bin]# ./fdfs_upload_file /opt/fastdfs/fastdfs-6.06/conf/client.conf /root/Downloads/libfastcommon-master.zip</span><br><span class="line">group1/M00/00/00/CoCqFmIUhZyAImx7AAPZehNXZm0384.zip</span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://github.com/happyfish100/fastdfs/wiki/">https://github.com/happyfish100/fastdfs/wiki/</a></p><p><a href="https://www.cnblogs.com/cnmenglang/p/6731209.html">FastDFS集群部署</a></p><p><a href="https://www.jianshu.com/p/1c71ae024e5e">FASTDFS</a></p><p><a href="https://www.jianshu.com/p/a842358b451b">Centos7下FastDFS集群搭建</a></p><p><a href="https://www.psvmc.cn/article/2020-09-18-hdfs-fastdfs.html">分布式文件系统（HDFS和FastDFS）</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了 FastDFS 的安装部署过程。首先，需要准备好设备环境，并安装相关依赖。接着，按照以下步骤进行安装：下载 FastDFS，解压安装，编辑配置文件，创建 system 服务，启动服务，并查看状态。最后，进行 Client 测试，测试上传文件，返回 ID 表示成功。&lt;/p&gt;</summary>
    
    
    
    <category term="middleware" scheme="https://www.wylu.me/categories/middleware/"/>
    
    <category term="distributed storage" scheme="https://www.wylu.me/categories/middleware/distributed-storage/"/>
    
    
    <category term="FastDFS" scheme="https://www.wylu.me/tags/FastDFS/"/>
    
  </entry>
  
  <entry>
    <title>二进制部署 k8s 集群 1.27.3 版本</title>
    <link href="https://www.wylu.me/posts/f5d33b91/"/>
    <id>https://www.wylu.me/posts/f5d33b91/</id>
    <published>2023-08-07T16:11:56.000Z</published>
    <updated>2023-08-07T16:24:11.062Z</updated>
    
    <content type="html"><![CDATA[<p>通过本文的指导，读者可以了解如何通过二进制的方式部署 Kubernetes 1.27.3 版本集群。二进制部署可以加深对 Kubernetes 各组件的理解，可以灵活地将各个组件部署到不同的机器，以满足自身的要求。但是需要注意的是，二进制部署需要手动配置各个组件，需要一定的技术水平和经验。</p><span id="more"></span><h1 id="二进制部署-k8s-集群-1.27.3-版本">二进制部署 k8s 集群 1.27.3 版本</h1><h2 id="环境准备">1. 环境准备</h2><p>虽然 <a href="https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/">kubeadm</a>, <a href="https://kubernetes.io/zh/docs/setup/production-environment/tools/kops/">kops</a>, <a href="https://kubernetes.io/zh/docs/setup/production-environment/tools/kubespray/">kubespray</a> 以及 <a href="https://docs.rancher.cn/rke/">rke</a>, <a href="https://kubesphere.io/zh/docs/">kubesphere</a> 等工具可以快速部署 k8s 集群，但是依然会有很多人热衷与使用二进制部署 k8s 集群。</p><p>二进制部署可以加深对 k8s 各组件的理解，可以灵活地将各个组件部署到不同的机器，以满足自身的要求。还可以生成一个超长时间自签证书，比如 99 年，免去忘记更新证书过期带来的生产事故。</p><h3 id="书写约定">1.1 书写约定</h3><ul><li>命令行输入，均以 <code>➜</code> 符号表示</li><li>注释使用 <code>#</code> 或 <code>//</code> 表示</li><li>执行命令输出结果，以空行分隔</li><li>如无特殊说明，命令需要在全部集群节点执行</li></ul><h3 id="机器规划">1.2 机器规划</h3><h4 id="操作系统">1.2.1 操作系统</h4><p><a href="https://download.rockylinux.org/pub/rocky/8/isos/x86_64/Rocky-8.6-x86_64-minimal.iso">Rocky-8.6-x86_64-minimal.iso</a></p><h4 id="集群节点">1.2.2 集群节点</h4><table><colgroup><col style="width: 25%" /><col style="width: 25%" /><col style="width: 25%" /><col style="width: 25%" /></colgroup><thead><tr class="header"><th>角色</th><th>主机名</th><th>IP</th><th>组件</th></tr></thead><tbody><tr class="odd"><td>master</td><td>master1</td><td>10.128.170.21</td><td>etcd, kube-apiserver, kube-controller-manager, kubelet, kube-proxy, kube-scheduler</td></tr><tr class="even"><td>worker</td><td>worker1</td><td>10.128.170.131</td><td>kubelet, kube-proxy</td></tr><tr class="odd"><td>worker</td><td>worker2</td><td>10.128.170.132</td><td>kubelet, kube-proxy</td></tr><tr class="even"><td>worker</td><td>worker3</td><td>10.128.170.133</td><td>kubelet, kube-proxy</td></tr></tbody></table><h4 id="测试节点可选">1.2.3 测试节点（可选）</h4><table><thead><tr class="header"><th>角色</th><th>主机名</th><th>IP</th><th>组件</th></tr></thead><tbody><tr class="odd"><td>registry</td><td>registry</td><td>10.128.170.235</td><td>docker</td></tr></tbody></table><h3 id="环境配置">1.3 环境配置</h3><h4 id="基础设置">1.3.1 基础设置</h4><ul><li><p>设置主机名</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在 master1 节点执行</span></span><br><span class="line">➜ hostnamectl set-hostname master1</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在 worker1 节点执行</span></span><br><span class="line">➜ hostnamectl set-hostname worker1</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在 worker2 节点执行</span></span><br><span class="line">➜ hostnamectl set-hostname worker2</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在 worker3 节点执行</span></span><br><span class="line">➜ hostnamectl set-hostname worker3</span><br></pre></td></tr></table></figure></li><li><p>主机名解析</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt;&gt; /etc/hosts &lt;&lt; EOF</span><br><span class="line">10.128.170.21 master1 master1.local</span><br><span class="line">10.128.170.131 worker1 worker1.local</span><br><span class="line">10.128.170.132 worker2 worker2.local</span><br><span class="line">10.128.170.133 worker3 worker3.local</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure></li><li><p>免密登录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在 master1 节点上执行（允许 master1 免密登录其他节点）</span></span><br><span class="line">➜ ssh-keygen -t rsa</span><br><span class="line">➜ ssh-copy-id worker1</span><br><span class="line">➜ ssh-copy-id worker2</span><br><span class="line">➜ ssh-copy-id worker3</span><br></pre></td></tr></table></figure></li><li><p>设置 yum 源</p><p><a href="https://developer.aliyun.com/mirror/rockylinux" class="uri">https://developer.aliyun.com/mirror/rockylinux</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">执行以下命令替换默认源</span></span><br><span class="line">➜ sed -e &#x27;s|^mirrorlist=|#mirrorlist=|g&#x27; \</span><br><span class="line">    -e &#x27;s|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g&#x27; \</span><br><span class="line">    -i.bak \</span><br><span class="line">    /etc/yum.repos.d/Rocky-*.repo</span><br><span class="line"></span><br><span class="line">➜ dnf makecache</span><br></pre></td></tr></table></figure></li><li><p>设置 epel 源</p><p><a href="https://developer.aliyun.com/mirror/epel" class="uri">https://developer.aliyun.com/mirror/epel</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">➜ yum install -y https://mirrors.aliyun.com/epel/epel-release-latest-8.noarch.rpm</span><br><span class="line">➜ sed -i &#x27;s|^#baseurl=https://download.example/pub|baseurl=https://mirrors.aliyun.com|&#x27; /etc/yum.repos.d/epel*</span><br><span class="line">➜ sed -i &#x27;s|^metalink|#metalink|&#x27; /etc/yum.repos.d/epel*</span><br></pre></td></tr></table></figure></li><li><p>安装必要工具</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ yum install -y vim wget htop</span><br></pre></td></tr></table></figure></li><li><p>创建下载文件存放目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir -p ~/Downloads</span><br></pre></td></tr></table></figure></li></ul><h4 id="k8s-环境设置">1.3.2 k8s 环境设置</h4><ul><li><p>关闭防火墙</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl stop firewalld</span><br><span class="line">➜ systemctl disable firewalld</span><br></pre></td></tr></table></figure></li><li><p>关闭 selinux</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">临时</span></span><br><span class="line">➜ setenforce 0</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">永久</span></span><br><span class="line">➜ sed -i &#x27;s/enforcing/disabled/&#x27; /etc/selinux/config</span><br></pre></td></tr></table></figure></li><li><p>关闭 swap</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">临时</span></span><br><span class="line">➜ swapoff -a</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">永久</span></span><br><span class="line">➜ sed -ri &#x27;s/.*swap.*/#&amp;/&#x27; /etc/fstab</span><br></pre></td></tr></table></figure><p>使用 <code>-r</code> 选项可以使用扩展正则表达式，这提供了一种更强大和灵活的方式来匹配文本中的模式。</p><p>使用正则表达式 <code>.*swap.*</code> 匹配包含 swap 字符串的行，并在行首添加 # 符号，&amp; 表示匹配到的整个字符串。</p></li><li><p>设置文件描述符限制</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">临时</span></span><br><span class="line">➜ ulimit -SHn 65535</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">永久</span></span><br><span class="line">➜ echo &quot;*      -      nofile   65535&quot; &gt;&gt;/etc/security/limits.conf</span><br></pre></td></tr></table></figure><p>用于设置当前用户的最大文件描述符数限制。具体来说，它的作用是将当前用户的软限制和硬限制都设置为 65535。</p></li><li><p>时间同步</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置时区</span></span><br><span class="line">➜ timedatectl set-timezone Asia/Shanghai</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装时间同步服务</span></span><br><span class="line">➜ yum install -y chrony</span><br><span class="line">➜ systemctl enable --now chronyd</span><br></pre></td></tr></table></figure></li><li><p>创建 kubernetes 证书存放目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir -p /etc/kubernetes/pki</span><br></pre></td></tr></table></figure></li></ul><h4 id="网络环境设置">1.3.3 网络环境设置</h4><ul><li><p>转发 IPv4 并让 iptables 看到桥接流量</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">加载 br_netfilter 和 overlay 模块</span></span><br><span class="line">➜ cat &gt; /etc/modules-load.d/k8s.conf &lt;&lt; EOF</span><br><span class="line">overlay</span><br><span class="line">br_netfilter</span><br><span class="line">EOF</span><br><span class="line">➜ modprobe overlay</span><br><span class="line">➜ modprobe br_netfilter</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置所需的 sysctl 参数，参数在重新启动后保持不变</span></span><br><span class="line">➜ cat &gt; /etc/sysctl.d/k8s.conf &lt;&lt; EOF</span><br><span class="line">net.bridge.bridge-nf-call-ip6tables = 1</span><br><span class="line">net.bridge.bridge-nf-call-iptables = 1</span><br><span class="line">net.ipv4.ip_forward = 1</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">应用 sysctl 参数而不重新启动</span></span><br><span class="line">➜ sysctl --system</span><br></pre></td></tr></table></figure><p>通过以下命令确认 br_netfilter 和 overlay 模块被加载：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">➜ lsmod | grep overlay</span><br><span class="line">➜ lsmod | grep br_netfilter</span><br></pre></td></tr></table></figure><p>通过以下命令确认 net.bridge.bridge-nf-call-iptables、net.bridge.bridge-nf-call-ip6tables 和 net.ipv4.ip_forward 系统变量在你的 sysctl 配置中被设置为 1：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward</span><br></pre></td></tr></table></figure></li><li><p>加载 ipvs 模块</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">➜ yum install -y ipset ipvsadm</span><br><span class="line">➜ cat &gt; /etc/sysconfig/modules/ipvs.modules &lt;&lt; &quot;EOF&quot;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/bash</span></span><br><span class="line">modprobe -- ip_vs</span><br><span class="line">modprobe -- ip_vs_rr</span><br><span class="line">modprobe -- ip_vs_wrr</span><br><span class="line">modprobe -- ip_vs_sh</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">modprobe -- nf_conntrack_ipv4</span></span><br><span class="line">modprobe -- nf_conntrack</span><br><span class="line">EOF</span><br><span class="line">➜ chmod +x /etc/sysconfig/modules/ipvs.modules</span><br><span class="line">➜ /bin/bash /etc/sysconfig/modules/ipvs.modules</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ lsmod | grep -e ip_vs -e nf_conntrack_ipv4</span></span><br><span class="line">➜ lsmod | grep -e ip_vs -e nf_conntrack</span><br></pre></td></tr></table></figure><ul><li>modprobe -- ip_vs: 加载 ip_vs 内核模块，该模块提供了 Linux 内核中的 IP 负载均衡功能。</li><li>modprobe -- ip_vs_rr: 加载 ip_vs_rr 内核模块，该模块提供了基于轮询算法的 IP 负载均衡策略。</li><li>modprobe -- ip_vs_wrr: 加载 ip_vs_wrr 内核模块，该模块提供了基于加权轮询算法的 IP 负载均衡策略。</li><li>modprobe -- ip_vs_sh: 加载 ip_vs_sh 内核模块，该模块提供了基于哈希算法的 IP 负载均衡策略。</li><li>modprobe -- nf_conntrack/nf_conntrack_ipv4: 加载 nf_conntrack/nf_conntrack_ipv4 内核模块，该模块提供了 Linux 内核中的网络连接跟踪功能，用于跟踪网络连接的状态。</li></ul><p>这些命令通常用于配置 Linux 系统中的负载均衡和网络连接跟踪功能。在加载这些内核模块之后，就可以使用相应的工具和命令来配置和管理负载均衡和网络连接跟踪。例如，可以使用 ipvsadm 命令来配置 IP 负载均衡，使用 conntrack 命令来查看和管理网络连接跟踪表。</p><p>如果提示如下错误：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;modprobe: FATAL: Module nf_conntrack_ipv4 not found in directory /lib/modules/4.18.0-372.9.1.el8.x86_64&quot;</span><br></pre></td></tr></table></figure><p>则需要将 <code>nf_conntrack_ipv4</code> 修改为 <code>nf_conntrack</code>，然后重新执行命令，因为在高版本内核中已经把 nf_conntrack_ipv4 替换为 nf_conntrack。</p><p>nf_conntrack_ipv4 和 nf_conntrack 都是 Linux 内核中的网络连接跟踪模块，用于跟踪网络连接的状态。它们的区别在于：</p><ul><li>nf_conntrack_ipv4 模块只能跟踪 IPv4 协议的网络连接，而 nf_conntrack 模块可以跟踪 IPv4 和 IPv6 协议的网络连接。</li><li>nf_conntrack_ipv4 模块是 nf_conntrack 模块的一个子模块，它提供了 IPv4 协议的网络连接跟踪功能。因此，如果要使用 nf_conntrack_ipv4 模块，必须先加载 nf_conntrack 模块。</li></ul><p>这两个模块通常用于 Linux 系统中的网络安全和网络性能优化。它们可以被用于防火墙、负载均衡、网络流量分析等场景中，以便对网络连接进行跟踪、监控和控制。例如，可以使用 iptables 命令和 nf_conntrack 模块来实现基于连接状态的防火墙规则，或者使用 ipvsadm 命令和 nf_conntrack 模块来实现 IP 负载均衡。</p><ul><li><a href="https://www.cnblogs.com/xiangsikai/p/9525287.html">Linux 跟踪连接netfilter 调优</a></li><li><a href="https://clodfisher.github.io/2018/09/nf_conntrack/">Iptables之nf_conntrack模块</a></li></ul></li></ul><h4 id="重启系统">1.3.4 重启系统</h4><ul><li><p>重启</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ reboot</span><br></pre></td></tr></table></figure></li></ul><h3 id="下载二进制包">1.4 下载二进制包</h3><p><a href="https://kubernetes.io/zh-cn/releases/download/" class="uri">https://kubernetes.io/zh-cn/releases/download/</a></p><p>从官方发布地址下载二进制包，下载 <a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.27.md#server-binaries">Server Binaries</a> 即可，这个包含了所有所需的二进制文件。</p><p><a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.27.md" class="uri">https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.27.md</a></p><p>解压后，复制二进制 <code>kube-apiserver,kube-controller-manager,kubectl,kubelet,kube-proxy,kube-scheduler</code> 到 master 节点 <code>/usr/local/bin</code> 目录下，复制二进制 <code>kubelet,kube-proxy</code> 到 worker 节点 <code>/usr/local/bin</code> 目录下。</p><p>在 master1 节点执行以下命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载</span></span><br><span class="line">➜ wget -c https://dl.k8s.io/v1.27.3/kubernetes-server-linux-amd64.tar.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">解压</span></span><br><span class="line">➜ tar -zxf kubernetes-server-linux-amd64.tar.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 master1 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ cp kubernetes/server/bin/&#123;kubeadm,kube-apiserver,kube-controller-manager,kubectl,kubelet,kube-proxy,kube-scheduler&#125; /usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看复制结果</span></span><br><span class="line">➜ ls -lh /usr/local/bin/kube*</span><br><span class="line"></span><br><span class="line">-rwxr-xr-x 1 root root  46M Jul 10 14:28 /usr/local/bin/kubeadm</span><br><span class="line">-rwxr-xr-x 1 root root 112M Jul 10 14:28 /usr/local/bin/kube-apiserver</span><br><span class="line">-rwxr-xr-x 1 root root 104M Jul 10 14:28 /usr/local/bin/kube-controller-manager</span><br><span class="line">-rwxr-xr-x 1 root root  47M Jul 10 14:28 /usr/local/bin/kubectl</span><br><span class="line">-rwxr-xr-x 1 root root 102M Jul 10 14:28 /usr/local/bin/kubelet</span><br><span class="line">-rwxr-xr-x 1 root root  51M Jul 10 14:28 /usr/local/bin/kube-proxy</span><br><span class="line">-rwxr-xr-x 1 root root  52M Jul 10 14:28 /usr/local/bin/kube-scheduler</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker1 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp kubernetes/server/bin/&#123;kubelet,kube-proxy&#125; root@worker1:/usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker2 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp kubernetes/server/bin/&#123;kubelet,kube-proxy&#125; root@worker2:/usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker3 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp kubernetes/server/bin/&#123;kubelet,kube-proxy&#125; root@worker3:/usr/local/bin</span><br></pre></td></tr></table></figure><h3 id="查看镜像版本">1.5 查看镜像版本</h3><p>在 master1 节点执行以下命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看依赖的镜像版本</span></span><br><span class="line">➜ kubeadm config images list</span><br><span class="line"></span><br><span class="line">registry.k8s.io/kube-apiserver:v1.27.3</span><br><span class="line">registry.k8s.io/kube-controller-manager:v1.27.3</span><br><span class="line">registry.k8s.io/kube-scheduler:v1.27.3</span><br><span class="line">registry.k8s.io/kube-proxy:v1.27.3</span><br><span class="line">registry.k8s.io/pause:3.9</span><br><span class="line">registry.k8s.io/etcd:3.5.7-0</span><br><span class="line">registry.k8s.io/coredns/coredns:v1.10.1</span><br></pre></td></tr></table></figure><h2 id="容器运行时">2. 容器运行时</h2><p>本节概述了使用 containerd 作为 CRI 运行时的必要步骤。</p><p><strong><a href="https://blog.51cto.com/lajifeiwomoshu/5428345" class="uri">https://blog.51cto.com/lajifeiwomoshu/5428345</a></strong></p><h3 id="安装-containerd">2.1 安装 containerd</h3><p>参考 <a href="https://github.com/containerd/containerd/blob/main/docs/getting-started.md">Getting started with containerd</a> 在各节点安装 containerd。</p><ul><li><p>设置 repository</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装 yum-utils</span></span><br><span class="line">➜ yum install -y yum-utils</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加 repository</span></span><br><span class="line">➜ yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo</span><br></pre></td></tr></table></figure><p>或者</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加 repository</span></span><br><span class="line">➜ dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo</span><br></pre></td></tr></table></figure></li><li><p>查看当前镜像源中支持的 containerd 版本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ yum list containerd.io --showduplicates</span><br></pre></td></tr></table></figure></li><li><p>安装特定版本的 containerd</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ yum install -y --setopt=obsoletes=0 containerd.io-1.6.21</span><br></pre></td></tr></table></figure></li><li><p>添加 containerd 配置</p><p><a href="https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd" class="uri">https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd</a></p><p><a href="https://github.com/containerd/containerd/blob/main/docs/cri/config.md" class="uri">https://github.com/containerd/containerd/blob/main/docs/cri/config.md</a></p><blockquote><p>This document provides the description of the CRI plugin configuration. The CRI plugin config is part of the containerd config (default path: <code>/etc/containerd/config.toml</code>).</p><p>See <a href="https://github.com/containerd/containerd/blob/main/docs/ops.md">here</a> for more information about containerd config.</p><p>Note that the <code>[plugins."io.containerd.grpc.v1.cri"]</code> section is specific to CRI, and not recognized by other containerd clients such as <code>ctr</code>, <code>nerdctl</code>, and Docker/Moby.</p></blockquote><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 containerd 配置文件</span></span><br><span class="line">➜ cat &gt; /etc/containerd/config.toml &lt;&lt; EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">https://github.com/containerd/containerd/blob/main/docs/cri/config.md</span></span><br><span class="line">disabled_plugins = []</span><br><span class="line">imports = []</span><br><span class="line">version = 2</span><br><span class="line"></span><br><span class="line">[plugins.&quot;io.containerd.grpc.v1.cri&quot;]</span><br><span class="line">  sandbox_image = &quot;registry.k8s.io/pause:3.9&quot;</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">https://github.com/containerd/containerd/issues/6964</span></span><br><span class="line">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.containerd.runtimes.runc]</span><br><span class="line">  runtime_type = &quot;io.containerd.runc.v2&quot;</span><br><span class="line">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.containerd.runtimes.runc.options]</span><br><span class="line">  SystemdCgroup = true</span><br><span class="line">[plugins.&quot;io.containerd.grpc.v1.cri&quot;.registry]</span><br><span class="line">  config_path = &quot;/etc/containerd/certs.d&quot;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建镜像仓库配置目录</span></span><br><span class="line">➜ mkdir -p /etc/containerd/certs.d</span><br></pre></td></tr></table></figure></li><li><p>启动 containerd</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line">➜ systemctl enable --now containerd</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看 containerd 状态</span></span><br><span class="line">➜ systemctl status containerd</span><br></pre></td></tr></table></figure></li><li><p>查看当前 containerd 使用的配置</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ containerd config dump</span><br></pre></td></tr></table></figure></li><li><p>测试 containerd（在任意一个集群节点测试即可）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">拉取 redis 镜像</span></span><br><span class="line">➜ ctr images pull docker.io/library/redis:alpine</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 redis 容器并运行</span></span><br><span class="line">➜ ctr run docker.io/library/redis:alpine redis</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除 redis 镜像</span></span><br><span class="line">➜ ctr images delete docker.io/library/redis:alpine</span><br></pre></td></tr></table></figure></li></ul><h3 id="安装-containerd-cli-工具">2.2 安装 containerd cli 工具</h3><p>There are several command line interface (CLI) projects for interacting with containerd:</p><table><colgroup><col style="width: 7%" /><col style="width: 18%" /><col style="width: 5%" /><col style="width: 15%" /><col style="width: 52%" /></colgroup><thead><tr class="header"><th>Name</th><th>Community</th><th>API</th><th>Target</th><th>Web site</th></tr></thead><tbody><tr class="odd"><td><code>ctr</code></td><td>containerd</td><td>Native</td><td>For debugging only</td><td>(None, see <code>ctr --help</code> to learn the usage)</td></tr><tr class="even"><td><code>nerdctl</code></td><td>containerd (non-core)</td><td>Native</td><td>General-purpose</td><td><a href="https://github.com/containerd/nerdctl" class="uri">https://github.com/containerd/nerdctl</a></td></tr><tr class="odd"><td><code>crictl</code></td><td>Kubernetes SIG-node</td><td>CRI</td><td>For debugging only</td><td><a href="https://github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md" class="uri">https://github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md</a></td></tr></tbody></table><h4 id="ctr">2.2.1 ctr</h4><p>While the <code>ctr</code> tool is bundled together with containerd, it should be noted the <code>ctr</code> tool is solely made for debugging containerd. The <a href="https://github.com/containerd/nerdctl"><code>nerdctl</code></a> tool provides stable and human-friendly user experience.</p><h4 id="crictl">2.2.2 crictl</h4><p>crictl 是 CRI 兼容的容器运行时命令行接口，可以使用它来检查和调试 k8s 节点上的容器运行时和应用程序。主要是用于 kubernetes， 默认操作的命名空间是 k8s.io，而且看到的对象是 pod。</p><p>如果是 k8s 环境的话，可以参考下方链接，在每个节点部署 containerd 的时候也部署下 crictl 工具。</p><p><a href="https://github.com/kubernetes-sigs/cri-tools">kubernetes-sigs/cri-tools: CLI and validation tools for Kubelet Container Runtime Interface (CRI) .</a></p><p>在 master1 节点执行以下命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载</span></span><br><span class="line">➜ wget -c https://hub.gitmirror.com/https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.27.1/crictl-v1.27.1-linux-amd64.tar.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">解压到 master1 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ tar -zxvf crictl-v1.27.1-linux-amd64.tar.gz -C /usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker1 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp /usr/local/bin/crictl root@worker1:/usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker2 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp /usr/local/bin/crictl root@worker2:/usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker3 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp /usr/local/bin/crictl root@worker3:/usr/local/bin</span><br></pre></td></tr></table></figure><p>创建 crictl 配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/crictl.yaml &lt;&lt; EOF</span><br><span class="line">runtime-endpoint: unix:///var/run/containerd/containerd.sock</span><br><span class="line">image-endpoint: unix:///var/run/containerd/containerd.sock</span><br><span class="line">timeout: 30</span><br><span class="line">debug: false</span><br><span class="line">EOF</span><br><span class="line">➜ scp /etc/crictl.yaml root@worker1:/etc</span><br><span class="line">➜ scp /etc/crictl.yaml root@worker2:/etc</span><br><span class="line">➜ scp /etc/crictl.yaml root@worker3:/etc</span><br></pre></td></tr></table></figure><p>测试 crictl 工具：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ crictl info --output go-template --template &#x27;&#123;&#123;.config.sandboxImage&#125;&#125;&#x27;</span><br><span class="line"></span><br><span class="line">registry.k8s.io/pause:3.9</span><br><span class="line"></span><br><span class="line">➜ crictl inspecti --output go-template --template &#x27;&#123;&#123;.status.pinned&#125;&#125;&#x27; registry.k8s.io/pause:3.9</span><br><span class="line"></span><br><span class="line">FATA[0000] no such image &quot;registry.k8s.io/pause:3.9&quot; present</span><br></pre></td></tr></table></figure><h4 id="nerdctl推荐">2.2.3 nerdctl（推荐）</h4><p><a href="https://github.com/containerd/nerdctl" class="uri">https://github.com/containerd/nerdctl</a></p><p>对于单机节点来说，推荐使用 nerdctl，使用起来和 docker 类似，基本没学习成本，把 docker 换成 nerdctl 即可。对于普通用户来说，这个是比较友好的工具。但是对于 api 那些来说，可以选择其他。</p><p>nerdctl 有两种版本：</p><ul><li>Minimal (<code>nerdctl-1.4.0-linux-amd64.tar.gz</code>): nerdctl only</li><li>Full (<code>nerdctl-full-1.4.0-linux-amd64.tar.gz</code>): Includes dependencies such as containerd, runc, and CNI</li></ul><p>这里我选择的是 Minimal 的版本，直接到 <a href="https://github.com/containerd/nerdctl/releases" class="uri">https://github.com/containerd/nerdctl/releases</a> 下载。</p><p>在 master1 节点执行以下命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载</span></span><br><span class="line">➜ wget -c https://hub.gitmirror.com/https://github.com/containerd/nerdctl/releases/download/v1.4.0/nerdctl-1.4.0-linux-amd64.tar.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">解压到 master1 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ tar Cxzvvf /usr/local/bin nerdctl-1.4.0-linux-amd64.tar.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker1 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp /usr/local/bin/&#123;containerd-rootless-setuptool.sh,containerd-rootless.sh,nerdctl&#125; root@worker1:/usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker2 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp /usr/local/bin/&#123;containerd-rootless-setuptool.sh,containerd-rootless.sh,nerdctl&#125; root@worker2:/usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 worker3 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ scp /usr/local/bin/&#123;containerd-rootless-setuptool.sh,containerd-rootless.sh,nerdctl&#125; root@worker3:/usr/local/bin</span><br></pre></td></tr></table></figure><p>测试 nerdctl（在任意一个集群节点测试即可）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">拉取镜像</span></span><br><span class="line">➜ nerdctl image pull redis:alpine</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看镜像</span></span><br><span class="line">➜ nerdctl image ls</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除镜像</span></span><br><span class="line">➜ nerdctl image rm redis:alpine</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">运行 nginx 服务（需要 CNI plugin）</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ nerdctl run -d --name nginx -p 80:80 nginx:alpine</span></span><br></pre></td></tr></table></figure><h3 id="设置公共仓库镜像源可选">2.3 设置公共仓库镜像源（可选）</h3><p><a href="https://github.com/containerd/containerd/blob/main/docs/cri/registry.md" class="uri">https://github.com/containerd/containerd/blob/main/docs/cri/registry.md</a></p><p><a href="https://github.com/containerd/containerd/blob/main/docs/hosts.md" class="uri">https://github.com/containerd/containerd/blob/main/docs/hosts.md</a></p><p>由于某些因素，在国内拉取公共镜像仓库的速度是极慢的，为了节约拉取时间，需要为 containerd 配置镜像仓库的 mirror。</p><p>containerd 的镜像仓库 mirror 与 docker 相比有两个区别：</p><ul><li><p>containerd 只支持通过 CRI 拉取镜像的 mirror，也就是说，只有通过 crictl，nerdctl 或者 kubernetes 调用时 mirror 才会生效，要想使用 ctr 拉取也生效的话需要指定 <code>--hosts-dir</code>。</p><blockquote><p>可以通过 <code>nerdctl --debug pull</code> 来观察</p></blockquote></li><li><p>docker 只支持为 Docker Hub 配置 mirror，而 containerd 支持为任意镜像仓库配置 mirror。</p><blockquote><p>registry.aliyuncs.com/google_containers 虽然有 k8s.gcr.io 的镜像，但不是加速站点</p></blockquote></li></ul><p>拉取镜像时，默认都是从 docker hub 上拉取，如果镜像名前不加 registry 地址的话默认会给你加上 <code>docker.io/library</code>。</p><p>需要注意的是：</p><ul><li>如果 hosts.toml 文件中的 capabilities 中不加 resolve 的话，无法加速镜像</li><li>配置无需重启服务，即可生效</li><li>要配保底的加速站点，否则可能会导致下载失败</li></ul><h4 id="docker.io">2.3.1 docker.io</h4><p><a href="https://yeasy.gitbook.io/docker_practice/install/mirror" class="uri">https://yeasy.gitbook.io/docker_practice/install/mirror</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 docker.io 目录</span></span><br><span class="line">➜ mkdir -p /etc/containerd/certs.d/docker.io</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 docker.io 仓库配置文件</span></span><br><span class="line">➜ cat &gt; /etc/containerd/certs.d/docker.io/hosts.toml &lt;&lt; EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">https://blog.csdn.net/qq_44797987/article/details/112681224</span></span><br><span class="line">server = &quot;https://docker.io&quot;</span><br><span class="line"></span><br><span class="line">[host.&quot;https://dockerproxy.com&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">[host.&quot;https://ccr.ccs.tencentyun.com&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">[host.&quot;https://hub-mirror.c.163.com&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">[host.&quot;https://mirror.baidubce.com&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">[host.&quot;https://registry-1.docker.io&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;, &quot;push&quot;]</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><h4 id="registry.k8s.io">2.3.2 registry.k8s.io</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 registry.k8s.io 目录</span></span><br><span class="line">➜ mkdir -p /etc/containerd/certs.d/registry.k8s.io</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 registry.k8s.io 仓库配置文件</span></span><br><span class="line">➜ cat &gt; /etc/containerd/certs.d/registry.k8s.io/hosts.toml &lt;&lt; EOF</span><br><span class="line">server = &quot;https://registry.k8s.io&quot;</span><br><span class="line"></span><br><span class="line">[host.&quot;https://registry.aliyuncs.com/v2/google_containers&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">  override_path = true</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>registry.aliyuncs.com/google_containers 这个镜像仓库站点不是 registry.k8s.io 的 mirror，只是有 registry.k8s.io 的镜像，这就是为什么 registry.k8s.io 有些镜像在 registry.aliyuncs.com/google_containers 没有的原因。</p><p>通过执行以下命令并观察输出可以知道，从 "registry.aliyuncs.com/google_containers" 拉取 pause 镜像正确的请求是 "<a href="https://registry.cn-hangzhou.aliyuncs.com/v2/google_containers/pause/manifests/3.9" class="uri">https://registry.cn-hangzhou.aliyuncs.com/v2/google_containers/pause/manifests/3.9</a>"。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">在 master1 节点进行测试</span></span><br><span class="line">➜ ctr --debug images pull -k registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line">DEBU[0000] do request                                    host=registry.cn-hangzhou.aliyuncs.com request.header.accept=&quot;application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*&quot; request.header.user-agent=containerd/1.6.21 request.method=HEAD url=&quot;https://registry.cn-hangzhou.aliyuncs.com/v2/google_containers/pause/manifests/3.9&quot;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>使用 ctr 命令拉取 "registry.k8s.io/pause:3.9" 镜像，如果最终拼接出的请求不是 "<a href="https://registry.cn-hangzhou.aliyuncs.com/v2/google_containers/pause/manifests/3.9" class="uri">https://registry.cn-hangzhou.aliyuncs.com/v2/google_containers/pause/manifests/3.9</a>"，则会导致拉取失败。</p><p>根据文档 <a href="https://github.com/containerd/containerd/blob/main/docs/hosts.md" class="uri">https://github.com/containerd/containerd/blob/main/docs/hosts.md</a> 可以知道：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pull [registry_host_name|IP address][:port][/v2][/org_path]&lt;image_name&gt;[:tag|@DIGEST]</span><br></pre></td></tr></table></figure><p>拉取请求格式的 <code>/v2</code> 部分指的是分发 api 的版本。 如果未包含在拉取请求中，则默认情况下为符合上面链接的分发规范的所有客户端添加 <code>/v2</code>。这可能会导致不合规的 OCI registry 使用有问题。</p><p>以拉取 "registry.k8s.io/pause:3.9" 镜像为例，<strong>在 master1 节点上进行测试</strong>，分析几种配置情况下实际的拉取请求 URL 及拉取结果：</p><h5 id="配置一错误">2.3.2.1 配置一（错误）</h5><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置 registry.k8s.io 配置</span></span><br><span class="line">➜ cat &gt; /etc/containerd/certs.d/registry.k8s.io/hosts.toml &lt;&lt; EOF</span><br><span class="line">server = &quot;https://registry.k8s.io&quot;</span><br><span class="line"></span><br><span class="line">[host.&quot;https://registry.aliyuncs.com/google_containers&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">测试镜像拉取</span></span><br><span class="line">➜ ctr --debug images pull --hosts-dir &quot;/etc/containerd/certs.d&quot; registry.k8s.io/pause:3.9</span><br><span class="line"></span><br><span class="line">DEBU[0000] fetching                                      image=&quot;registry.k8s.io/pause:3.9&quot;</span><br><span class="line">DEBU[0000] loading host directory                        dir=/etc/containerd/certs.d/registry.k8s.io</span><br><span class="line">DEBU[0000] resolving                                     host=registry.aliyuncs.com</span><br><span class="line">DEBU[0000] do request                                    host=registry.aliyuncs.com request.header.accept=&quot;application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*&quot; request.header.user-agent=containerd/1.6.21 request.method=HEAD url=&quot;https://registry.aliyuncs.com/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] fetch response received                       host=registry.aliyuncs.com response.header.content-length=19 response.header.content-type=&quot;text/plain; charset=utf-8&quot; response.header.date=&quot;Wed, 12 Jul 2023 14:40:38 GMT&quot; response.header.docker-distribution-api-version=registry/2.0 response.header.x-content-type-options=nosniff response.status=&quot;404 Not Found&quot; url=&quot;https://registry.aliyuncs.com/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">INFO[0000] trying next host - response was http.StatusNotFound  host=registry.aliyuncs.com</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>由输出可知，因为实际请求的 URL "<a href="https://registry.aliyuncs.com/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io" class="uri">https://registry.aliyuncs.com/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io</a>" 不正确，所以拉取失败。</p><h5 id="配置二错误">2.3.2.2 配置二（错误）</h5><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置 registry.k8s.io 配置</span></span><br><span class="line">➜ cat &gt; /etc/containerd/certs.d/registry.k8s.io/hosts.toml &lt;&lt; EOF</span><br><span class="line">server = &quot;https://registry.k8s.io&quot;</span><br><span class="line"></span><br><span class="line">[host.&quot;https://registry.aliyuncs.com/v2/google_containers&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">测试镜像拉取</span></span><br><span class="line">➜ ctr --debug images pull --hosts-dir &quot;/etc/containerd/certs.d&quot; registry.k8s.io/pause:3.9</span><br><span class="line"></span><br><span class="line">DEBU[0000] fetching                                      image=&quot;registry.k8s.io/pause:3.9&quot;</span><br><span class="line">DEBU[0000] loading host directory                        dir=/etc/containerd/certs.d/registry.k8s.io</span><br><span class="line">DEBU[0000] resolving                                     host=registry.aliyuncs.com</span><br><span class="line">DEBU[0000] do request                                    host=registry.aliyuncs.com request.header.accept=&quot;application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*&quot; request.header.user-agent=containerd/1.6.21 request.method=HEAD url=&quot;https://registry.aliyuncs.com/v2/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] fetch response received                       host=registry.aliyuncs.com response.header.content-length=169 response.header.content-type=&quot;application/json; charset=utf-8&quot; response.header.date=&quot;Wed, 12 Jul 2023 15:02:21 GMT&quot; response.header.docker-distribution-api-version=registry/2.0 response.header.www-authenticate=&quot;Bearer realm=\&quot;https://dockerauth.cn-hangzhou.aliyuncs.com/auth\&quot;,service=\&quot;registry.aliyuncs.com:cn-hangzhou:26842\&quot;,scope=\&quot;repository:google_containers/v2/pause:pull\&quot;&quot; response.status=&quot;401 Unauthorized&quot; url=&quot;https://registry.aliyuncs.com/v2/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] Unauthorized                                  header=&quot;Bearer realm=\&quot;https://dockerauth.cn-hangzhou.aliyuncs.com/auth\&quot;,service=\&quot;registry.aliyuncs.com:cn-hangzhou:26842\&quot;,scope=\&quot;repository:google_containers/v2/pause:pull\&quot;&quot; host=registry.aliyuncs.com</span><br><span class="line">DEBU[0000] do request                                    host=registry.aliyuncs.com request.header.accept=&quot;application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*&quot; request.header.user-agent=containerd/1.6.21 request.method=HEAD url=&quot;https://registry.aliyuncs.com/v2/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] fetch response received                       host=registry.aliyuncs.com response.header.content-length=169 response.header.content-type=&quot;application/json; charset=utf-8&quot; response.header.date=&quot;Wed, 12 Jul 2023 15:02:21 GMT&quot; response.header.docker-distribution-api-version=registry/2.0 response.header.www-authenticate=&quot;Bearer realm=\&quot;https://dockerauth.cn-hangzhou.aliyuncs.com/auth\&quot;,service=\&quot;registry.aliyuncs.com:cn-hangzhou:26842\&quot;,scope=\&quot;repository:google_containers/v2/pause:pull\&quot;,error=\&quot;insufficient_scope\&quot;&quot; response.status=&quot;401 Unauthorized&quot; url=&quot;https://registry.aliyuncs.com/v2/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] Unauthorized                                  header=&quot;Bearer realm=\&quot;https://dockerauth.cn-hangzhou.aliyuncs.com/auth\&quot;,service=\&quot;registry.aliyuncs.com:cn-hangzhou:26842\&quot;,scope=\&quot;repository:google_containers/v2/pause:pull\&quot;,error=\&quot;insufficient_scope\&quot;&quot; host=registry.aliyuncs.com</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>由输出可知，因为实际请求的 URL "<a href="https://registry.aliyuncs.com/v2/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io" class="uri">https://registry.aliyuncs.com/v2/google_containers/v2/pause/manifests/3.9?ns=registry.k8s.io</a>" 不正确，所以拉取失败。</p><h5 id="配置三正确">2.3.2.3 配置三（正确）</h5><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置 registry.k8s.io 配置</span></span><br><span class="line">➜ cat &gt; /etc/containerd/certs.d/registry.k8s.io/hosts.toml &lt;&lt; EOF</span><br><span class="line">server = &quot;https://registry.k8s.io&quot;</span><br><span class="line"></span><br><span class="line">[host.&quot;https://registry.aliyuncs.com/v2/google_containers&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">  override_path = true</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">测试镜像拉取</span></span><br><span class="line">➜ ctr --debug images pull --hosts-dir &quot;/etc/containerd/certs.d&quot; registry.k8s.io/pause:3.9</span><br><span class="line"></span><br><span class="line">DEBU[0000] fetching                                      image=&quot;registry.k8s.io/pause:3.9&quot;</span><br><span class="line">DEBU[0000] loading host directory                        dir=/etc/containerd/certs.d/registry.k8s.io</span><br><span class="line">DEBU[0000] resolving                                     host=registry.aliyuncs.com</span><br><span class="line">DEBU[0000] do request                                    host=registry.aliyuncs.com request.header.accept=&quot;application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*&quot; request.header.user-agent=containerd/1.6.21 request.method=HEAD url=&quot;https://registry.aliyuncs.com/v2/google_containers/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] fetch response received                       host=registry.aliyuncs.com response.header.content-length=166 response.header.content-type=&quot;application/json; charset=utf-8&quot; response.header.date=&quot;Wed, 12 Jul 2023 15:04:06 GMT&quot; response.header.docker-distribution-api-version=registry/2.0 response.header.www-authenticate=&quot;Bearer realm=\&quot;https://dockerauth.cn-hangzhou.aliyuncs.com/auth\&quot;,service=\&quot;registry.aliyuncs.com:cn-hangzhou:26842\&quot;,scope=\&quot;repository:google_containers/pause:pull\&quot;&quot; response.status=&quot;401 Unauthorized&quot; url=&quot;https://registry.aliyuncs.com/v2/google_containers/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] Unauthorized                                  header=&quot;Bearer realm=\&quot;https://dockerauth.cn-hangzhou.aliyuncs.com/auth\&quot;,service=\&quot;registry.aliyuncs.com:cn-hangzhou:26842\&quot;,scope=\&quot;repository:google_containers/pause:pull\&quot;&quot; host=registry.aliyuncs.com</span><br><span class="line">DEBU[0000] do request                                    host=registry.aliyuncs.com request.header.accept=&quot;application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, */*&quot; request.header.user-agent=containerd/1.6.21 request.method=HEAD url=&quot;https://registry.aliyuncs.com/v2/google_containers/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] fetch response received                       host=registry.aliyuncs.com response.header.content-length=2405 response.header.content-type=application/vnd.docker.distribution.manifest.list.v2+json response.header.date=&quot;Wed, 12 Jul 2023 15:04:07 GMT&quot; response.header.docker-content-digest=&quot;sha256:7031c1b283388d2c2e09b57badb803c05ebed362dc88d84b480cc47f72a21097&quot; response.header.docker-distribution-api-version=registry/2.0 response.header.etag=&quot;\&quot;sha256:7031c1b283388d2c2e09b57badb803c05ebed362dc88d84b480cc47f72a21097\&quot;&quot; response.status=&quot;200 OK&quot; url=&quot;https://registry.aliyuncs.com/v2/google_containers/pause/manifests/3.9?ns=registry.k8s.io&quot;</span><br><span class="line">DEBU[0000] resolved                                      desc.digest=&quot;sha256:7031c1b283388d2c2e09b57badb803c05ebed362dc88d84b480cc47f72a21097&quot; host=registry.aliyuncs.com</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>由输出可知，因为实际请求的 URL "<a href="https://registry.aliyuncs.com/v2/google_containers/pause/manifests/3.9?ns=registry.k8s.io" class="uri">https://registry.aliyuncs.com/v2/google_containers/pause/manifests/3.9?ns=registry.k8s.io</a>" 正确，所以拉取成功。</p><p><a href="https://github.com/containerd/containerd/blob/main/docs/hosts.md#override_path-field" class="uri">https://github.com/containerd/containerd/blob/main/docs/hosts.md#override_path-field</a></p><p><code>override_path</code> is used to indicate the host's API root endpoint is defined in the URL path rather than by the API specification. This may be used with non-compliant OCI registries which are missing the <code>/v2</code> prefix. (Defaults to <code>false</code>)</p><h4 id="quay.io">2.3.3 quay.io</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 quay.io 目录</span></span><br><span class="line">➜ mkdir -p /etc/containerd/certs.d/quay.io</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 quay.io 仓库配置文件</span></span><br><span class="line">➜ cat &gt; /etc/containerd/certs.d/quay.io/hosts.toml &lt;&lt; EOF</span><br><span class="line">server = &quot;https://quay.io&quot;</span><br><span class="line"></span><br><span class="line">[host.&quot;https://quay-mirror.qiniu.com&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;]</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><h3 id="设置私有仓库可选">2.4 设置私有仓库（可选）</h3><h4 id="部署私有仓库">2.4.1 部署私有仓库</h4><p><strong>在用于测试的 registry 节点部署私有仓库，以测试 containerd 使用私有仓库的场景。</strong></p><h5 id="安装-docker">2.4.1.1 安装 docker</h5><p>参考文档 <a href="https://docs.docker.com/engine/install/centos/">Install Docker Engine on CentOS</a>，在 registry 节点安装 docker：</p><ul><li><p>切换镜像源</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo</span><br></pre></td></tr></table></figure><p>或者</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加 repository</span></span><br><span class="line">➜ dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo</span><br></pre></td></tr></table></figure></li><li><p>查看当前镜像源中支持的 docker 版本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ yum list docker-ce --showduplicates</span><br></pre></td></tr></table></figure></li><li><p>安装特定版本的 docker</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ yum install -y --setopt=obsoletes=0 docker-ce-20.10.24</span><br></pre></td></tr></table></figure><p><code>--setopt=obsoletes=0</code> 是 yum 包管理器的一个选项，它的作用是禁用软件包依赖关系中的版本升级。</p></li><li><p>添加配置文件</p><p>docker 在默认情况下使用的 cgroup driver 为 cgroupfs，而 kubernetes 推荐使用 systemd 来替代 cgroupfs。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir /etc/docker</span><br><span class="line">➜ cat &gt; /etc/docker/daemon.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">  &quot;exec-opts&quot;: [&quot;native.cgroupdriver=systemd&quot;],</span><br><span class="line">  &quot;registry-mirrors&quot;: [&quot;https://mirror.baidubce.com&quot;, &quot;https://hub-mirror.c.163.com&quot;]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure></li><li><p>启动 dokcer</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line">➜ systemctl enable --now docker</span><br><span class="line">➜ systemctl status docker</span><br></pre></td></tr></table></figure></li></ul><h5 id="部署-registry">2.4.1.2 部署 registry</h5><p><a href="https://docs.docker.com/registry/deploying/" class="uri">https://docs.docker.com/registry/deploying/</a></p><ul><li><p>拉取 registry 镜像</p><p><a href="https://hub.docker.com/_/registry" class="uri">https://hub.docker.com/_/registry</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ docker pull registry:2.8.1</span><br></pre></td></tr></table></figure></li><li><p>创建 registry 数据目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir /data</span><br></pre></td></tr></table></figure></li><li><p>运行 registry 服务</p><p><a href="https://yeasy.gitbook.io/docker_practice/repository/registry" class="uri">https://yeasy.gitbook.io/docker_practice/repository/registry</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">➜ docker run -d \</span><br><span class="line">    --name registry.local \</span><br><span class="line">    --publish 5000:5000 \</span><br><span class="line">    --restart always \</span><br><span class="line">    --volume /data:/var/lib/registry \</span><br><span class="line">    registry:2.8.1</span><br></pre></td></tr></table></figure></li><li><p>拉取、推送测试镜像</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">拉取镜像</span></span><br><span class="line">➜ docker pull redis:alpine</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">标记镜像</span></span><br><span class="line">➜ docker tag redis:alpine registry.local:5000/redis:alpine</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">推送镜像</span></span><br><span class="line">➜ docker push registry.local:5000/redis:alpine</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看镜像</span></span><br><span class="line">➜ curl registry.local:5000/v2/_catalog</span><br></pre></td></tr></table></figure></li></ul><h4 id="registry.local">2.4.2 registry.local</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 registry.local 目录</span></span><br><span class="line">➜ mkdir -p /etc/containerd/certs.d/registry.local</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 registry.local 仓库配置文件</span></span><br><span class="line">➜ cat &gt; /etc/containerd/certs.d/registry.local/hosts.toml &lt;&lt; EOF</span><br><span class="line">server = &quot;https://registry.local&quot;</span><br><span class="line"></span><br><span class="line">[host.&quot;http://registry.local:5000&quot;]</span><br><span class="line">  capabilities = [&quot;pull&quot;, &quot;resolve&quot;, &quot;push&quot;]</span><br><span class="line">  skip_verify = true</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">追加 /etc/hosts 配置</span></span><br><span class="line">➜ cat &gt;&gt; /etc/hosts &lt;&lt; EOF</span><br><span class="line">10.128.170.235 registry.local</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><h4 id="测试私有仓库">2.4.3 测试私有仓库</h4><p>在任意一个集群节点测试即可：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">拉取镜像</span></span><br><span class="line">➜ ctr --debug images pull --hosts-dir &quot;/etc/containerd/certs.d&quot; registry.local/redis:alpine</span><br></pre></td></tr></table></figure><p>需要注意的是：</p><ul><li><p><code>ctr image pull</code> 的时候需要注意镜像名称需要完整，否则无法拉取，格式如下：</p><p><code>[registry_host_name|IP address][:port][/v2][/org_path]&lt;image_name&gt;[:tag|@DIGEST]</code></p></li><li><p>因为 ctr 不使用 CRI，所以默认不会使用 config.toml 中 cri 的配置，如果拉取镜像时希望使用 mirror，则需要指定 <code>--hosts-dir</code>。</p></li></ul><h2 id="创建-ca-证书">3. 创建 ca 证书</h2><p><strong>证书操作如无特殊说明，只需在 master1 节点执行即可。</strong></p><h3 id="安装-cfssl">3.1 安装 cfssl</h3><p>cfssl 是一款证书签署工具，使用 cfssl 工具可以很简化证书签署过程，方便颁发自签证书。</p><p>CloudFlare's distributes <a href="https://github.com/cloudflare/cfssl">cfssl</a> source code on github page and binaries on <a href="https://pkg.cfssl.org/">cfssl website</a>.</p><p>Our documentation assumes that you will run <a href="https://github.com/cloudflare/cfssl">cfssl</a> on your local x86_64 Linux host.</p><p><a href="https://github.com/cloudflare/cfssl/releases/tag/v1.6.4" class="uri">https://github.com/cloudflare/cfssl/releases/tag/v1.6.4</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载并重命名</span></span><br><span class="line">➜ curl -L -o https://download.nuaa.cf/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_amd64</span><br><span class="line">➜ curl -L -o /usr/local/bin/cfssljson https://download.nuaa.cf/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_amd64</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">赋予可执行权限</span></span><br><span class="line">➜ chmod +x /usr/local/bin/&#123;cfssl,cfssljson&#125;</span><br></pre></td></tr></table></figure><p>离线安装的情况，直接把两个文件下载下来重命名即可。</p><h3 id="创建-ca-证书-1">3.2 创建 ca 证书</h3><p>创建的证书统一放到 /etc/kubernetes/ssl 目录，创建后复制到 /etc/kubernetes/pki 目录。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ mkdir -p /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">ca 证书创建申请</span></span><br><span class="line">➜ cat &gt; ca-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;kubernetes&quot;,</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;k8s&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;ca&quot;: &#123;</span><br><span class="line">        &quot;expiry&quot;: &quot;87600h&quot;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 ca 证书</span></span><br><span class="line">➜ cfssl gencert -initca ca-csr.json | cfssljson -bare ca</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ls -lh ca*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 root root 1.7K Jul 13 12:38 ca-key.pem</span><br><span class="line">-rw-r--r-- 1 root root 1.4K Jul 13 12:38 ca.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 ca 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp ca*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><p>ca-csr.json 这个文件是 Kubernetes 集群中使用的根证书的签名请求 (CSR) 配置文件，用于定义 CA 证书的签名请求配置。</p><p>在这个配置文件中，CN 字段指定了证书的通用名称为 "kubernetes"，key 字段指定了证书的密钥算法为 RSA，密钥长度为 2048 位。names 字段定义了证书的其他信息，如国家、省份、城市、组织和组织单位等。ca 字段指定了证书的过期时间为 87600 小时（即 10 年）。</p><p>这个配置文件用于创建 Kubernetes 集群中的 CA 证书，以便对集群中的其他证书进行签名和认证。</p><ul><li>CN(Common Name): kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name)</li><li>names[].O(Organization): kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group)</li></ul><p>由于这里是 CA 证书，是签发其它证书的根证书，这个证书密钥不会分发出去作为 client 证书，所有组件使用的 client 证书都是由 CA 证书签发而来，所以 CA 证书的 CN 和 O 的名称并不重要，后续其它签发出来的证书的 CN 和 O 的名称才是有用的。</p><h3 id="创建签发配置文件">3.3 创建签发配置文件</h3><p>由于各个组件都需要配置证书，并且依赖 CA 证书来签发证书，所以我们首先要生成好 CA 证书以及后续的签发配置文件。</p><p>创建用于签发其它证书的配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">证书签发配置文件</span></span><br><span class="line">➜ cat &gt; ca-config.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;signing&quot;: &#123;</span><br><span class="line">        &quot;default&quot;: &#123;</span><br><span class="line">            &quot;expiry&quot;: &quot;87600h&quot;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;profiles&quot;: &#123;</span><br><span class="line">            &quot;kubernetes&quot;: &#123;</span><br><span class="line">                &quot;usages&quot;: [</span><br><span class="line">                    &quot;signing&quot;,</span><br><span class="line">                    &quot;key encipherment&quot;,</span><br><span class="line">                    &quot;server auth&quot;,</span><br><span class="line">                    &quot;client auth&quot;</span><br><span class="line">                ],</span><br><span class="line">                &quot;expiry&quot;: &quot;87600h&quot;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>ca-config.json 这个文件是签发其它证书的配置文件，用于定义签名配置和证书配置。其中，signing 字段定义了签名配置，profiles 字段定义了不同场景下的证书配置。</p><p>在这个配置文件中，default 配置指定了默认的证书过期时间为 87600 小时（即 10 年），profiles 配置定义了一个名为 "kubernetes" 的证书配置，它指定了证书的用途（签名、密钥加密、服务器认证和客户端认证）和过期时间。</p><p>这个配置文件用于创建 Kubernetes 集群中的证书和密钥，以便对集群进行安全认证和加密通信。</p><ul><li>signing：定义了签名配置，包括默认的签名过期时间和各个证书配置的签名过期时间。</li><li>profiles：定义了不同场景下的证书配置，包括证书的用途、过期时间和其他属性。</li></ul><p>在使用 cfssl gencert 命令生成证书时，可以使用 <code>-config</code> 参数指定配置文件，以便根据配置文件中的规则生成符合要求的证书。如果不指定 <code>-config</code> 参数，则 cfssl gencert 命令将使用默认的配置文件。</p><h2 id="部署-etcd">4. 部署 etcd</h2><p>根据 kubeadm 获取的信息，在 Kubernetes 1.27.3 版本中 etcd 使用的版本是 3.5.7，<strong>只需在 master 节点（也即 master1 节点）部署即可</strong>。</p><p><a href="https://github.com/etcd-io/etcd/releases/tag/v3.5.7" class="uri">https://github.com/etcd-io/etcd/releases/tag/v3.5.7</a></p><h3 id="颁发证书">4.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">etcd 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">hosts 字段中，IP 为所有 etcd 集群节点地址，这里可以做好规划，预留几个 IP，以备后续扩容。</span></span><br><span class="line">➜ cat &gt; etcd-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;etcd&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.21&quot;,</span><br><span class="line">        &quot;10.128.170.22&quot;,</span><br><span class="line">        &quot;10.128.170.23&quot;,</span><br><span class="line">        &quot;localhost&quot;,</span><br><span class="line">        &quot;master1&quot;,</span><br><span class="line">        &quot;master2&quot;,</span><br><span class="line">        &quot;master3&quot;,</span><br><span class="line">        &quot;master1.local&quot;,</span><br><span class="line">        &quot;master2.local&quot;,</span><br><span class="line">        &quot;master3.local&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;k8s&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 etcd 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes etcd-csr.json | cfssljson -bare etcd</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ls -lh etcd*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 root root 1.7K Jul 13 23:35 etcd-key.pem</span><br><span class="line">-rw-r--r-- 1 root root 1.6K Jul 13 23:35 etcd.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 etcd 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp etcd*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><h3 id="部署-etcd-1">4.2 部署 etcd</h3><p>下载二进制包 <a href="https://github.com/etcd-io/etcd/releases/tag/v3.5.7" class="uri">https://github.com/etcd-io/etcd/releases/tag/v3.5.7</a> 并解压，将二进制程序 <code>etcd</code> 和 <code>etcdctl</code> 复制到 <code>/usr/local/bin</code> 目录下。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载</span></span><br><span class="line">➜ wget -c https://hub.gitmirror.com/https://github.com/etcd-io/etcd/releases/download/v3.5.7/etcd-v3.5.7-linux-amd64.tar.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">解压</span></span><br><span class="line">➜ tar -zxf etcd-v3.5.7-linux-amd64.tar.gz</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制到 master1 节点 /usr/local/bin 目录</span></span><br><span class="line">➜ cp etcd-v3.5.7-linux-amd64/&#123;etcd,etcdctl&#125; /usr/local/bin</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看复制结果</span></span><br><span class="line">➜ ls -lh /usr/local/bin/etcd*</span><br><span class="line"></span><br><span class="line">-rwxr-xr-x 1 root root 22M Jul 13 23:49 /usr/local/bin/etcd</span><br><span class="line">-rwxr-xr-x 1 root root 17M Jul 13 23:49 /usr/local/bin/etcdctl</span><br></pre></td></tr></table></figure><p>编写服务配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir /etc/etcd</span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/etcd/etcd.conf &lt;&lt; EOF</span><br><span class="line">ETCD_NAME=&quot;etcd1&quot;</span><br><span class="line">ETCD_DATA_DIR=&quot;/var/lib/etcd/default.etcd&quot;</span><br><span class="line">ETCD_LISTEN_PEER_URLS=&quot;https://10.128.170.21:2380&quot;</span><br><span class="line">ETCD_LISTEN_CLIENT_URLS=&quot;https://10.128.170.21:2379&quot;</span><br><span class="line">ETCD_INITIAL_ADVERTISE_PEER_URLS=&quot;https://10.128.170.21:2380&quot;</span><br><span class="line">ETCD_ADVERTISE_CLIENT_URLS=&quot;https://10.128.170.21:2379&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER=&quot;etcd1=https://10.128.170.21:2380&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER_TOKEN=&quot;etcd-cluster&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER_STATE=&quot;new&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>配置文件解释：</p><ul><li><code>ETCD_NAME</code>：节点名称，集群中唯一</li><li><code>ETCD_DATA_DIR</code>： 数据保存目录</li><li><code>ETCD_LISTEN_PEER_URLS</code>：集群内部通信监听地址</li><li><code>ETCD_LISTEN_CLIENT_URLS</code>：客户端访问监听地址</li><li><code>ETCD_INITIAL_ADVERTISE_PEER_URLS</code>：集群通告地址</li><li><code>ETCD_ADVERTISE_CLIENT_URLS</code>：客户端通告地址</li><li><code>ETCD_INITIAL_CLUSTER</code>：集群节点地址列表</li><li><code>ETCD_INITIAL_CLUSTER_TOKEN</code>：集群通信 token</li><li><code>ETCD_INITIAL_CLUSTER_STATE</code>：加入集群的当前状态，new 是新集群，existing 表示加入已有集群</li></ul><p>编写服务启动脚本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建数据目录</span></span><br><span class="line">➜ mkdir -p /var/lib/etcd</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建系统服务</span></span><br><span class="line">➜ cat &gt; /lib/systemd/system/etcd.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=etcd server</span><br><span class="line">After=network.target</span><br><span class="line">After=network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=notify</span><br><span class="line">EnvironmentFile=/etc/etcd/etcd.conf</span><br><span class="line">WorkingDirectory=/var/lib/etcd</span><br><span class="line">ExecStart=/usr/local/bin/etcd \</span><br><span class="line">  --cert-file=/etc/kubernetes/pki/etcd.pem \</span><br><span class="line">  --key-file=/etc/kubernetes/pki/etcd-key.pem \</span><br><span class="line">  --trusted-ca-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --peer-cert-file=/etc/kubernetes/pki/etcd.pem \</span><br><span class="line">  --peer-key-file=/etc/kubernetes/pki/etcd-key.pem \</span><br><span class="line">  --peer-trusted-ca-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --peer-client-cert-auth \</span><br><span class="line">  --client-cert-auth</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 etcd 服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now etcd</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status etcd</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u etcd</span><br></pre></td></tr></table></figure><h2 id="部署-kube-apiserver">5. 部署 kube-apiserver</h2><p><strong>只需在 master 节点（也即 master1 节点）部署即可。</strong></p><h3 id="颁发证书-1">5.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kube-apiserver 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">hosts 字段中，IP 为所有 kube-apiserver 节点地址，这里可以做好规划，预留几个 IP，以备后续扩容。</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">10.96.0.1 是 service 网段的第一个 IP</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kubernetes.default.svc.cluster.local 是 kube-apiserver 的 service 域名</span></span><br><span class="line">➜ cat &gt; kube-apiserver-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;kubernetes&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.21&quot;,</span><br><span class="line">        &quot;10.128.170.22&quot;,</span><br><span class="line">        &quot;10.128.170.23&quot;,</span><br><span class="line">        &quot;10.96.0.1&quot;,</span><br><span class="line">        &quot;localhost&quot;,</span><br><span class="line">        &quot;master1&quot;,</span><br><span class="line">        &quot;master2&quot;,</span><br><span class="line">        &quot;master3&quot;,</span><br><span class="line">        &quot;master1.local&quot;,</span><br><span class="line">        &quot;master2.local&quot;,</span><br><span class="line">        &quot;master3.local&quot;,</span><br><span class="line">        &quot;kubernetes&quot;,</span><br><span class="line">        &quot;kubernetes.default&quot;,</span><br><span class="line">        &quot;kubernetes.default.svc&quot;,</span><br><span class="line">        &quot;kubernetes.default.svc.cluster&quot;,</span><br><span class="line">        &quot;kubernetes.default.svc.cluster.local&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;k8s&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kube-apiserver 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-apiserver-csr.json | cfssljson -bare kube-apiserver</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ls -lh kube-apiserver*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 root root 1.7K Jul 14 00:07 kube-apiserver-key.pem</span><br><span class="line">-rw-r--r-- 1 root root 1.8K Jul 14 00:07 kube-apiserver.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 kube-apiserver 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp kube-apiserver*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><h3 id="部署-kube-apiserver-1">5.2 部署 kube-apiserver</h3><p>编写服务配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">可以使用 kubeadm 生成示例配置文件，然后进行修改</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ kubeadm init phase control-plane apiserver --dry-run -v 4</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[control-plane] Creating static Pod manifest <span class="keyword">for</span> <span class="string">&quot;kube-apiserver&quot;</span></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">I0717 00:13:59.260175    6660 manifests.go:154] [control-plane] wrote static Pod manifest <span class="keyword">for</span> component <span class="string">&quot;kube-apiserver&quot;</span> to <span class="string">&quot;/etc/kubernetes/tmp/kubeadm-init-dryrun365914376/kube-apiserver.yaml&quot;</span></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/kube-apiserver.conf &lt;&lt; EOF</span><br><span class="line">KUBE_APISERVER_OPTS=&quot;--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \</span><br><span class="line">  --anonymous-auth=false \</span><br><span class="line">  --bind-address=0.0.0.0 \</span><br><span class="line">  --secure-port=6443 \</span><br><span class="line">  --authorization-mode=Node,RBAC \</span><br><span class="line">  --runtime-config=api/all=true \</span><br><span class="line">  --enable-bootstrap-token-auth \</span><br><span class="line">  --service-cluster-ip-range=10.96.0.0/16 \</span><br><span class="line">  --token-auth-file=/etc/kubernetes/token.csv \</span><br><span class="line">  --service-node-port-range=30000-32767 \</span><br><span class="line">  --tls-cert-file=/etc/kubernetes/pki/kube-apiserver.pem \</span><br><span class="line">  --tls-private-key-file=/etc/kubernetes/pki/kube-apiserver-key.pem \</span><br><span class="line">  --client-ca-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --kubelet-client-certificate=/etc/kubernetes/pki/kube-apiserver.pem \</span><br><span class="line">  --kubelet-client-key=/etc/kubernetes/pki/kube-apiserver-key.pem \</span><br><span class="line">  --service-account-key-file=/etc/kubernetes/pki/ca-key.pem \</span><br><span class="line">  --service-account-signing-key-file=/etc/kubernetes/pki/ca-key.pem \</span><br><span class="line">  --service-account-issuer=https://kubernetes.default.svc.cluster.local \</span><br><span class="line">  --etcd-cafile=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --etcd-certfile=/etc/kubernetes/pki/etcd.pem \</span><br><span class="line">  --etcd-keyfile=/etc/kubernetes/pki/etcd-key.pem \</span><br><span class="line">  --etcd-servers=https://10.128.170.21:2379 \</span><br><span class="line">  --allow-privileged=true \</span><br><span class="line">  --apiserver-count=1 \</span><br><span class="line">  --audit-log-maxage=30 \</span><br><span class="line">  --audit-log-maxbackup=3 \</span><br><span class="line">  --audit-log-maxsize=100 \</span><br><span class="line">  --audit-log-path=/var/log/kube-apiserver-audit.log \</span><br><span class="line">  --event-ttl=1h \</span><br><span class="line">  --v=4&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><blockquote><p>如果 etcd 是一个集群，则 <code>--etcd-servers</code> 可以添加多个，例如：<code>--etcd-servers=https://10.128.170.21:2379,https://10.128.170.22:2379,https://10.128.170.23:2379</code></p></blockquote><p>生成 token 文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/token.csv &lt;&lt; EOF</span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">(<span class="built_in">head</span> -c 16 /dev/urandom | <span class="built_in">od</span> -An -t x | <span class="built_in">tr</span> -d <span class="string">&#x27; &#x27;</span>),kubelet-bootstrap,10001,<span class="string">&quot;system:node-bootstrapper&quot;</span></span></span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><blockquote><p>在这个命令中，<code>head -c 16 /dev/urandom | od -An -t x | tr -d ' '</code> 生成了一个 16 字节的随机字符串，并将其转换为十六进制格式。这个字符串将作为令牌的值。</p><ul><li>kubelet-bootstrap 是令牌的用户名</li><li>10001 是令牌的 UID，</li><li>system:node-bootstrapper 是令牌的组名。</li></ul><p>这些值将用于 kubelet 节点的身份验证和授权。</p></blockquote><p>编写服务启动脚本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kube-apiserver.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes API Server</span><br><span class="line">Documentation=https://github.com/kubernetes/kubernetes</span><br><span class="line">After=network.target network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=notify</span><br><span class="line">EnvironmentFile=/etc/kubernetes/kube-apiserver.conf</span><br><span class="line">ExecStart=/usr/local/bin/kube-apiserver $KUBE_APISERVER_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kube-apiserver 服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-apiserver</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-apiserver</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-apiserver</span><br></pre></td></tr></table></figure><h2 id="配置-kubectl">6. 配置 kubectl</h2><p>部署完 kube-apiserver 后，就可以配置 kubectl 了，因为 kubectl 可以验证 kube-apiserver 是否已经正常工作了。</p><p><strong>只需在 master 节点（也即 master1 节点）配置即可。</strong></p><h3 id="颁发证书-2">6.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kubectl 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">O 参数的值必须为 system:masters，因为这是 kube-apiserver 一个内置好的角色，拥有集群管理的权限</span></span><br><span class="line">➜ cat &gt; kubectl-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;clusteradmin&quot;,</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;system:masters&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kubectl 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubectl-csr.json | cfssljson -bare kubectl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ls -lh kubectl*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 root root 1.7K Jul 14 00:45 kubectl-key.pem</span><br><span class="line">-rw-r--r-- 1 root root 1.4K Jul 14 00:45 kubectl.pem</span><br></pre></td></tr></table></figure><h3 id="生成配置文件">6.2 生成配置文件</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.21:6443 --kubeconfig=kube.config</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials clusteradmin --client-certificate=kubectl.pem --client-key=kubectl-key.pem --embed-certs=true --kubeconfig=kube.config</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context kubernetes --cluster=kubernetes --user=clusteradmin --kubeconfig=kube.config</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context kubernetes --kubeconfig=kube.config</span><br><span class="line"></span><br><span class="line">➜ mkdir ~/.kube</span><br><span class="line"></span><br><span class="line">➜ cp kube.config ~/.kube/config</span><br></pre></td></tr></table></figure><p>以上命令用于在本地创建一个 Kubernetes 配置文件 kube.config，并将其复制到 ~/.kube/config 文件中，以便使用 kubectl 命令与 Kubernetes 集群进行交互。</p><blockquote><p>kubectl config set-cluster 命令设置了一个名为 kubernetes 的集群，指定了以下参数：</p><ul><li><code>--certificate-authority=ca.pem</code>：指定 CA 证书文件的路径。</li><li><code>--embed-certs=true</code>：将 CA 证书嵌入到配置文件中。</li><li><code>--server=https://10.128.170.21:6443</code>：指定 API Server 的地址和端口。</li><li><code>--kubeconfig=kube.config</code>：指定要写入的配置文件路径。</li></ul><p>这些参数将用于创建一个名为 kubernetes 的集群配置，并将其写入到 kube.config 文件中。</p><p>kubectl config set-credentials 命令设置了一个名为 clusteradmin 的用户，指定了以下参数：</p><ul><li><code>--client-certificate=kubectl.pem</code>：指定客户端证书文件的路径。</li><li><code>--client-key=kubectl-key.pem</code>：指定客户端私钥文件的路径。</li><li><code>--embed-certs=true</code>：将客户端证书和私钥嵌入到配置文件中。</li><li><code>--kubeconfig=kube.config</code>：指定要写入的配置文件路径。</li></ul><p>这些参数将用于创建一个名为 clusteradmin 的用户配置，并将其写入到 kube.config 文件中。</p><p>kubectl config set-context 命令设置了一个名为 kubernetes 的上下文，指定了以下参数：</p><ul><li><code>--cluster=kubernetes</code>：指定要使用的集群。</li><li><code>--user=clusteradmin</code>：指定要使用的用户。</li><li><code>--kubeconfig=kube.config</code>：指定要写入的配置文件路径。</li></ul><p>这些参数将用于创建一个名为 kubernetes 的上下文配置，并将其写入到 kube.config 文件中。</p><p>kubectl config use-context 命令将当前上下文设置为 kubernetes，指定了以下参数：</p><ul><li><code>--kubeconfig=kube.config</code>：指定要使用的配置文件路径。</li></ul><p>这个命令将当前上下文设置为 kubernetes，以便 kubectl 命令可以使用 kube.config 文件与 Kubernetes 集群进行交互。</p></blockquote><h3 id="获取集群信息">6.3 获取集群信息</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl cluster-info</span><br><span class="line"></span><br><span class="line">Kubernetes control plane is running at https://10.128.170.21:6443</span><br><span class="line"></span><br><span class="line">To further debug and diagnose cluster problems, use &#x27;kubectl cluster-info dump&#x27;.</span><br><span class="line"></span><br><span class="line">➜ kubectl get all -A</span><br><span class="line"></span><br><span class="line">NAMESPACE   NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE</span><br><span class="line">default     service/kubernetes   ClusterIP   10.96.0.1    &lt;none&gt;        443/TCP   22m</span><br><span class="line"></span><br><span class="line">➜ kubectl get cs</span><br><span class="line"></span><br><span class="line">Warning: v1 ComponentStatus is deprecated in v1.19+</span><br><span class="line">NAME                 STATUS      MESSAGE                                                                                        ERROR</span><br><span class="line">controller-manager   Unhealthy   Get &quot;https://127.0.0.1:10257/healthz&quot;: dial tcp 127.0.0.1:10257: connect: connection refused</span><br><span class="line">scheduler            Unhealthy   Get &quot;https://127.0.0.1:10259/healthz&quot;: dial tcp 127.0.0.1:10259: connect: connection refused</span><br><span class="line">etcd-0               Healthy     &#123;&quot;health&quot;:&quot;true&quot;,&quot;reason&quot;:&quot;&quot;&#125;</span><br></pre></td></tr></table></figure><h3 id="设置-kubectl-自动补全">6.4 设置 kubectl 自动补全</h3><p>查看 kubectl 命令自动补全帮助：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl completion --help</span><br><span class="line">````</span><br><span class="line"></span><br><span class="line">安装 bash-completion：</span><br><span class="line"></span><br><span class="line">```shell</span><br><span class="line">➜ yum install -y bash-completion</span><br></pre></td></tr></table></figure><p>设置 kubectl 自动补全配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ echo &quot;source &lt;(kubectl completion bash)&quot; &gt;&gt; ~/.bashrc</span><br><span class="line">````</span><br><span class="line"></span><br><span class="line">使配置生效：</span><br><span class="line"></span><br><span class="line">```shell</span><br><span class="line">➜ source ~/.bashrc</span><br></pre></td></tr></table></figure><h2 id="部署-kube-controller-manager">7. 部署 kube-controller-manager</h2><p><strong>只需在 master 节点（也即 master1 节点）部署即可。</strong></p><h3 id="颁发证书-3">7.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kube-controller-manager 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">hosts 字段中，IP 为所有节点地址，这里可以做好规划，预留几个 IP，以备后续扩容。</span></span><br><span class="line">➜ cat &gt; kube-controller-manager-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;system:kube-controller-manager&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.21&quot;,</span><br><span class="line">        &quot;10.128.170.22&quot;,</span><br><span class="line">        &quot;10.128.170.23&quot;,</span><br><span class="line">        &quot;10.128.170.131&quot;,</span><br><span class="line">        &quot;10.128.170.132&quot;,</span><br><span class="line">        &quot;10.128.170.133&quot;,</span><br><span class="line">        &quot;10.128.170.134&quot;,</span><br><span class="line">        &quot;10.128.170.135&quot;,</span><br><span class="line">        &quot;localhost&quot;,</span><br><span class="line">        &quot;master1&quot;,</span><br><span class="line">        &quot;master2&quot;,</span><br><span class="line">        &quot;master3&quot;,</span><br><span class="line">        &quot;worker1&quot;,</span><br><span class="line">        &quot;worker2&quot;,</span><br><span class="line">        &quot;worker3&quot;,</span><br><span class="line">        &quot;worker4&quot;,</span><br><span class="line">        &quot;worker5&quot;,</span><br><span class="line">        &quot;master1.local&quot;,</span><br><span class="line">        &quot;master2.local&quot;,</span><br><span class="line">        &quot;master3.local&quot;,</span><br><span class="line">        &quot;worker1.local&quot;,</span><br><span class="line">        &quot;worker2.local&quot;,</span><br><span class="line">        &quot;worker3.local&quot;,</span><br><span class="line">        &quot;worker4.local&quot;,</span><br><span class="line">        &quot;worker5.local&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;system:kube-controller-manager&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kube-controller-manager 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ls -lh kube-controller-manager*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 root root 1.7K Jul 14 00:55 kube-controller-manager-key.pem</span><br><span class="line">-rw-r--r-- 1 root root 1.8K Jul 14 00:55 kube-controller-manager.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 kube-controler-manager 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp kube-controller-manager*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><p>system:kube-controller-manager 是 Kubernetes 中的一个预定义 RBAC 角色，用于授权 kube-controller-manager 组件对 Kubernetes API 的访问。详细介绍请参考官方文档：<a href="https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/#default-roles-and-role-bindings" class="uri">https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/#default-roles-and-role-bindings</a></p><h3 id="部署-kube-controller-manager-1">7.2 部署 kube-controller-manager</h3><p>编写服务配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">可以使用 kubeadm 生成示例配置文件，然后进行修改</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ kubeadm init phase control-plane controller-manager --dry-run -v 4</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[control-plane] Creating static Pod manifest <span class="keyword">for</span> <span class="string">&quot;kube-controller-manager&quot;</span></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">I0717 00:18:23.798277    6694 manifests.go:154] [control-plane] wrote static Pod manifest <span class="keyword">for</span> component <span class="string">&quot;kube-controller-manager&quot;</span> to <span class="string">&quot;/etc/kubernetes/tmp/kubeadm-init-dryrun963226442/kube-controller-manager.yaml&quot;</span></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/kube-controller-manager.conf &lt;&lt; EOF</span><br><span class="line">KUBE_CONTROLLER_MANAGER_OPTS=&quot;--secure-port=10257 \</span><br><span class="line">  --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \</span><br><span class="line">  --service-cluster-ip-range=10.96.0.0/16 \</span><br><span class="line">  --cluster-name=kubernetes \</span><br><span class="line">  --cluster-signing-cert-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --cluster-signing-key-file=/etc/kubernetes/pki/ca-key.pem \</span><br><span class="line">  --cluster-signing-duration=87600h \</span><br><span class="line">  --tls-cert-file=/etc/kubernetes/pki/kube-controller-manager.pem \</span><br><span class="line">  --tls-private-key-file=/etc/kubernetes/pki/kube-controller-manager-key.pem \</span><br><span class="line">  --service-account-private-key-file=/etc/kubernetes/pki/ca-key.pem \</span><br><span class="line">  --root-ca-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --leader-elect=true \</span><br><span class="line">  --controllers=*,bootstrapsigner,tokencleaner \</span><br><span class="line">  --use-service-account-credentials=true \</span><br><span class="line">  --horizontal-pod-autoscaler-sync-period=10s \</span><br><span class="line">  --allocate-node-cidrs=true \</span><br><span class="line">  --cluster-cidr=10.240.0.0/12 \</span><br><span class="line">  --v=4&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>生成 kubeconfig：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.21:6443 --kubeconfig=kube-controller-manager.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials kube-controller-manager --client-certificate=kube-controller-manager.pem --client-key=kube-controller-manager-key.pem --embed-certs=true --kubeconfig=kube-controller-manager.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context default --cluster=kubernetes --user=kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ cp kube-controller-manager.kubeconfig /etc/kubernetes/</span><br></pre></td></tr></table></figure><p>编写服务启动脚本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kube-controller-manager.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes controller manager</span><br><span class="line">Documentation=https://github.com/kubernetes/kubernetes</span><br><span class="line">After=network.target network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">EnvironmentFile=/etc/kubernetes/kube-controller-manager.conf</span><br><span class="line">ExecStart=/usr/local/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kube-controller-manager 服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-controller-manager</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-controller-manager</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-controller-manager</span><br></pre></td></tr></table></figure><p>查看组件状态：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get cs</span><br><span class="line"></span><br><span class="line">Warning: v1 ComponentStatus is deprecated in v1.19+</span><br><span class="line">NAME                 STATUS      MESSAGE                                                                                        ERROR</span><br><span class="line">scheduler            Unhealthy   Get &quot;https://127.0.0.1:10259/healthz&quot;: dial tcp 127.0.0.1:10259: connect: connection refused</span><br><span class="line">controller-manager   Healthy     ok</span><br><span class="line">etcd-0               Healthy     &#123;&quot;health&quot;:&quot;true&quot;,&quot;reason&quot;:&quot;&quot;&#125;</span><br></pre></td></tr></table></figure><h2 id="部署-kube-scheduler">8. 部署 kube-scheduler</h2><p><strong>只需在 master 节点（也即 master1 节点）部署即可。</strong></p><h3 id="颁发证书-4">8.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kube-scheduler 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">hosts 字段中，IP 为所有节点地址，这里可以做好规划，预留几个 IP，以备后续扩容。</span></span><br><span class="line">➜ cat &gt; kube-scheduler-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;system:kube-scheduler&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.21&quot;,</span><br><span class="line">        &quot;10.128.170.22&quot;,</span><br><span class="line">        &quot;10.128.170.23&quot;,</span><br><span class="line">        &quot;10.128.170.131&quot;,</span><br><span class="line">        &quot;10.128.170.132&quot;,</span><br><span class="line">        &quot;10.128.170.133&quot;,</span><br><span class="line">        &quot;10.128.170.134&quot;,</span><br><span class="line">        &quot;10.128.170.135&quot;,</span><br><span class="line">        &quot;localhost&quot;,</span><br><span class="line">        &quot;master1&quot;,</span><br><span class="line">        &quot;master2&quot;,</span><br><span class="line">        &quot;master3&quot;,</span><br><span class="line">        &quot;worker1&quot;,</span><br><span class="line">        &quot;worker2&quot;,</span><br><span class="line">        &quot;worker3&quot;,</span><br><span class="line">        &quot;worker4&quot;,</span><br><span class="line">        &quot;worker5&quot;,</span><br><span class="line">        &quot;master1.local&quot;,</span><br><span class="line">        &quot;master2.local&quot;,</span><br><span class="line">        &quot;master3.local&quot;,</span><br><span class="line">        &quot;worker1.local&quot;,</span><br><span class="line">        &quot;worker2.local&quot;,</span><br><span class="line">        &quot;worker3.local&quot;,</span><br><span class="line">        &quot;worker4.local&quot;,</span><br><span class="line">        &quot;worker5.local&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;system:kube-scheduler&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kube-scheduler 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-scheduler-csr.json | cfssljson -bare kube-scheduler</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ls -lh kube-scheduler*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 root root 1.7K Jul 14 01:06 kube-scheduler-key.pem</span><br><span class="line">-rw-r--r-- 1 root root 1.8K Jul 14 01:06 kube-scheduler.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 kube-scheduler 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp kube-scheduler*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><h3 id="部署-kube-scheduler-1">8.2 部署 kube-scheduler</h3><p>编写服务配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">可以使用 kubeadm 生成示例配置文件，然后进行修改</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ kubeadm init phase control-plane scheduler --dry-run -v 4</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[control-plane] Creating static Pod manifest <span class="keyword">for</span> <span class="string">&quot;kube-scheduler&quot;</span></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">I0717 00:26:08.548412    6903 manifests.go:154] [control-plane] wrote static Pod manifest <span class="keyword">for</span> component <span class="string">&quot;kube-scheduler&quot;</span> to <span class="string">&quot;/etc/kubernetes/tmp/kubeadm-init-dryrun1609159078/kube-scheduler.yaml&quot;</span></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/kube-scheduler.conf &lt;&lt; EOF</span><br><span class="line">KUBE_SCHEDULER_OPTS=&quot;--bind-address=127.0.0.1 \</span><br><span class="line">  --kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \</span><br><span class="line">  --leader-elect=true \</span><br><span class="line">  --v=4&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>生成 kubeconfig</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.21:6443 --kubeconfig=kube-scheduler.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials kube-scheduler --client-certificate=kube-scheduler.pem --client-key=kube-scheduler-key.pem --embed-certs=true --kubeconfig=kube-scheduler.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context default --cluster=kubernetes --user=kube-scheduler --kubeconfig=kube-scheduler.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ cp kube-scheduler.kubeconfig /etc/kubernetes/</span><br></pre></td></tr></table></figure><p>编写服务启动脚本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kube-scheduler.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes scheduler</span><br><span class="line">Documentation=https://github.com/kubernetes/kubernetes</span><br><span class="line">After=network.target network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">EnvironmentFile=/etc/kubernetes/kube-scheduler.conf</span><br><span class="line">ExecStart=/usr/local/bin/kube-scheduler $KUBE_SCHEDULER_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kube-scheduler 服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-scheduler</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-scheduler</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-scheduler</span><br></pre></td></tr></table></figure><p>查看组件状态：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get cs</span><br><span class="line"></span><br><span class="line">Warning: v1 ComponentStatus is deprecated in v1.19+</span><br><span class="line">NAME                 STATUS    MESSAGE                         ERROR</span><br><span class="line">controller-manager   Healthy   ok</span><br><span class="line">scheduler            Healthy   ok</span><br><span class="line">etcd-0               Healthy   &#123;&quot;health&quot;:&quot;true&quot;,&quot;reason&quot;:&quot;&quot;&#125;</span><br></pre></td></tr></table></figure><h2 id="部署-kubelet">9. 部署 kubelet</h2><p><strong>先在 master 节点完成部署，后续添加 worker 节点时从 master 节点复制配置并调整即可。</strong></p><p>master 节点上部署 kubelet 是可选的，一旦部署 kubelet，master 节点也可以运行 Pod，如果不希望 master 节点上运行 Pod，则可以给 master 节点打上污点。</p><p>master 节点部署 kubelet 是有好处的，一是可以通过诸如 <code>kubectl get node</code> 等命令查看节点信息，二是可以在上面部署监控系统，日志采集系统等。</p><h3 id="授权-kubelet-允许请求证书">9.1 授权 kubelet 允许请求证书</h3><p>授权 kubelet-bootstrap 用户允许请求证书：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"></span><br><span class="line">➜ kubectl create clusterrolebinding kubelet-bootstrap \</span><br><span class="line">--clusterrole=system:node-bootstrapper \</span><br><span class="line">--user=kubelet-bootstrap</span><br><span class="line"></span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/kubelet-bootstrap created</span><br></pre></td></tr></table></figure><h3 id="部署-kubelet-1">9.2 部署 kubelet</h3><p>编写服务配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">可以使用 kubeadm 生成示例配置文件，然后进行修改</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ kubeadm init phase kubelet-start --dry-run</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[kubelet-start] Writing kubelet environment file with flags to file <span class="string">&quot;/etc/kubernetes/tmp/kubeadm-init-dryrun3282605628/kubeadm-flags.env&quot;</span></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[kubelet-start] Writing kubelet configuration to file <span class="string">&quot;/etc/kubernetes/tmp/kubeadm-init-dryrun3282605628/config.yaml&quot;</span></span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">https://github.com/kubernetes-sigs/sig-windows-tools/issues/323</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">https://github.com/kubernetes/kubernetes/pull/118544</span></span><br><span class="line">➜ cat &gt; /etc/kubernetes/kubelet.conf &lt;&lt; EOF</span><br><span class="line">KUBELET_OPTS=&quot;--bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.kubeconfig \</span><br><span class="line">  --config=/etc/kubernetes/kubelet.yaml \</span><br><span class="line">  --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \</span><br><span class="line">  --cert-dir=/etc/kubernetes/pki \</span><br><span class="line">  --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \</span><br><span class="line">  --pod-infra-container-image=registry.k8s.io/pause:3.9 \</span><br><span class="line">  --v=4</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/kubelet.yaml &lt;&lt; EOF</span><br><span class="line">kind: KubeletConfiguration</span><br><span class="line">apiVersion: kubelet.config.k8s.io/v1beta1</span><br><span class="line">address: 0.0.0.0</span><br><span class="line">port: 10250</span><br><span class="line">readOnlyPort: 0</span><br><span class="line">authentication:</span><br><span class="line">  anonymous:</span><br><span class="line">    enabled: false</span><br><span class="line">  webhook:</span><br><span class="line">    cacheTTL: 2m0s</span><br><span class="line">    enabled: true</span><br><span class="line">  x509:</span><br><span class="line">    clientCAFile: /etc/kubernetes/pki/ca.pem</span><br><span class="line">authorization:</span><br><span class="line">  mode: Webhook</span><br><span class="line">  webhook:</span><br><span class="line">    cacheAuthorizedTTL: 5m0s</span><br><span class="line">    cacheUnauthorizedTTL: 30s</span><br><span class="line">cgroupDriver: systemd</span><br><span class="line">clusterDNS:</span><br><span class="line">- 10.96.0.10</span><br><span class="line">clusterDomain: cluster.local</span><br><span class="line">healthzBindAddress: 127.0.0.1</span><br><span class="line">healthzPort: 10248</span><br><span class="line">rotateCertificates: true</span><br><span class="line">evictionHard:</span><br><span class="line">  imagefs.available: 15%</span><br><span class="line">  memory.available: 100Mi</span><br><span class="line">  nodefs.available: 10%</span><br><span class="line">  nodefs.inodesFree: 5%</span><br><span class="line">maxOpenFiles: 1000000</span><br><span class="line">maxPods: 110</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>生成 kubeconfig：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.21:6443 --kubeconfig=kubelet-bootstrap.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials kubelet-bootstrap --token=$(awk -F, &#x27;&#123;print $1&#125;&#x27; /etc/kubernetes/token.csv) --kubeconfig=kubelet-bootstrap.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context default --cluster=kubernetes --user=kubelet-bootstrap --kubeconfig=kubelet-bootstrap.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context default --kubeconfig=kubelet-bootstrap.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ cp kubelet-bootstrap.kubeconfig /etc/kubernetes/</span><br></pre></td></tr></table></figure><p>编写服务启动脚本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kubelet.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes kubelet</span><br><span class="line">After=network.target network-online.targer containerd.service</span><br><span class="line">Requires=containerd.service</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">EnvironmentFile=/etc/kubernetes/kubelet.conf</span><br><span class="line">ExecStart=/usr/local/bin/kubelet $KUBELET_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kubelet 服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kubelet</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kubelet</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kubelet</span><br></pre></td></tr></table></figure><p>批准节点加入集群：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get csr</span><br><span class="line"></span><br><span class="line">NAME        AGE   SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION</span><br><span class="line">csr-h92vn   59s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   &lt;none&gt;              Pending</span><br><span class="line"></span><br><span class="line">➜ kubectl certificate approve csr-h92vn</span><br><span class="line"></span><br><span class="line">certificatesigningrequest.certificates.k8s.io/csr-h92vn approved</span><br><span class="line"></span><br><span class="line">➜ kubectl get csr</span><br><span class="line"></span><br><span class="line">NAME        AGE     SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION</span><br><span class="line">csr-h92vn   2m27s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   &lt;none&gt;              Approved,Issued</span><br></pre></td></tr></table></figure><p>查看节点：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get node</span><br><span class="line"></span><br><span class="line">NAME      STATUS     ROLES    AGE   VERSION</span><br><span class="line">master1   NotReady   &lt;none&gt;   71s   v1.27.3</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">此时节点状态还是 NotReady，因为还没有安装网络插件，正确安装网络插件后，状态会变为 Ready.</span></span><br></pre></td></tr></table></figure><h2 id="部署-kube-proxy">10. 部署 kube-proxy</h2><p><strong>先在 master 节点完成部署，后续添加 worker 节点时从 master 节点复制配置并调整即可。</strong></p><h3 id="颁发证书-5">10.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">进入 /etc/kubernetes/ssl 目录</span></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kube-proxy 证书签署申请</span></span><br><span class="line">➜ cat &gt; kube-proxy-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;system:kube-proxy&quot;,</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;k8s&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kube-proxy 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ls -lh kube-proxy*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 root root 1.7K Jul 15 18:36 kube-proxy-key.pem</span><br><span class="line">-rw-r--r-- 1 root root 1.4K Jul 15 18:36 kube-proxy.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 kube-proxy 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp kube-proxy*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><h3 id="部署-kube-proxy-1">10.2 部署 kube-proxy</h3><p>编写服务配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/kube-proxy.conf &lt;&lt; EOF</span><br><span class="line">KUBE_PROXY_OPTS=&quot;--config=/etc/kubernetes/kube-proxy.yaml \</span><br><span class="line">  --v=4</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/kube-proxy.yaml &lt;&lt; EOF</span><br><span class="line">kind: KubeProxyConfiguration</span><br><span class="line">apiVersion: kubeproxy.config.k8s.io/v1alpha1</span><br><span class="line">clientConnection:</span><br><span class="line">  kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig</span><br><span class="line">bindAddress: 0.0.0.0</span><br><span class="line">clusterCIDR: 10.240.0.0/12</span><br><span class="line">healthzBindAddress: 0.0.0.0:10256</span><br><span class="line">metricsBindAddress: 0.0.0.0:10249</span><br><span class="line">mode: ipvs</span><br><span class="line">ipvs:</span><br><span class="line">  scheduler: &quot;rr&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>生成 kubeconfig：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.21:6443 --kubeconfig=kube-proxy.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials kube-proxy --client-certificate=kube-proxy.pem --client-key=kube-proxy-key.pem --embed-certs=true --kubeconfig=kube-proxy.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context default --cluster=kubernetes --user=kube-proxy --kubeconfig=kube-proxy.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ cp kube-proxy.kubeconfig /etc/kubernetes/</span><br></pre></td></tr></table></figure><p>编写服务启动脚本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kube-proxy.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes Proxy</span><br><span class="line">Documentation=https://github.com/kubernetes/kubernetes</span><br><span class="line">After=network.target network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">EnvironmentFile=-/etc/kubernetes/kube-proxy.conf</span><br><span class="line">ExecStart=/usr/local/bin/kube-proxy $KUBE_PROXY_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kube-proxy 服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-proxy</span><br></pre></td></tr></table></figure><h2 id="部署集群网络">11. 部署集群网络</h2><p><strong>只需在 master 节点（也即 master1 节点）部署即可。</strong></p><h3 id="部署-calico">11.1 部署 calico</h3><p>参考文档 <a href="https://projectcalico.docs.tigera.io/getting-started/kubernetes/self-managed-onprem/onpremises">Install Calico networking and network policy for on-premises deployments</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line"></span><br><span class="line">➜ curl -k https://cdn.jsdelivr.net/gh/projectcalico/calico@v3.26.1/manifests/calico.yaml -O</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">找到 CALICO_IPV4POOL_CIDR 变量，取消注释并修改 Pod IP 地址段</span></span><br><span class="line">➜ sed -i &#x27;s/# \(- name: CALICO_IPV4POOL_CIDR\)/\1/&#x27; calico.yaml</span><br><span class="line">➜ sed -i &#x27;s/#   value: &quot;192.168.0.0\/16&quot;/  value: &quot;10.240.0.0\/12&quot;/&#x27; calico.yaml</span><br><span class="line"></span><br><span class="line">➜ kubectl apply -f calico.yaml</span><br><span class="line"></span><br><span class="line">poddisruptionbudget.policy/calico-kube-controllers created</span><br><span class="line">serviceaccount/calico-kube-controllers created</span><br><span class="line">serviceaccount/calico-node created</span><br><span class="line">serviceaccount/calico-cni-plugin created</span><br><span class="line">configmap/calico-config created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/bgpfilters.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/caliconodestatuses.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ipreservations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io/calico-node created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io/calico-cni-plugin created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/calico-node created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/calico-cni-plugin created</span><br><span class="line">daemonset.apps/calico-node created</span><br><span class="line">deployment.apps/calico-kube-controllers created</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看网络 pod</span></span><br><span class="line">➜ kubectl -n kube-system get pod</span><br><span class="line"></span><br><span class="line">NAME                                       READY   STATUS    RESTARTS   AGE</span><br><span class="line">calico-kube-controllers-85578c44bf-7v87p   1/1     Running   0          25m</span><br><span class="line">calico-node-v924z                          1/1     Running   0          25m</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看 node 状态</span></span><br><span class="line">➜ kubectl get node</span><br><span class="line"></span><br><span class="line">NAME      STATUS   ROLES    AGE   VERSION</span><br><span class="line">master1   Ready    &lt;none&gt;   30m   v1.27.3</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看 ipvs 模式</span></span><br><span class="line">➜ ipvsadm -Ln</span><br><span class="line"></span><br><span class="line">IP Virtual Server version 1.2.1 (size=4096)</span><br><span class="line">Prot LocalAddress:Port Scheduler Flags</span><br><span class="line"><span class="meta prompt_">  -&gt; </span><span class="language-bash">RemoteAddress:Port           Forward Weight ActiveConn InActConn</span></span><br><span class="line">TCP  10.96.0.1:443 rr</span><br><span class="line"><span class="meta prompt_">  -&gt; </span><span class="language-bash">10.128.170.21:6443           Masq    1      5          0</span></span><br></pre></td></tr></table></figure><p><strong>如果 node 状态仍然是 NotReady，基本上是镜像未拉取完成或拉取失败导致的，如果一段时间后仍拉取失败，则尝试手动拉取镜像。</strong></p><h3 id="授权-kube-apiserver-访问-kubelet">11.2 授权 kube-apiserver 访问 kubelet</h3><p><a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/">Using RBAC Authorization</a></p><p>应用场景：例如 kubectl exec/run/logs</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line"></span><br><span class="line">➜ cat &gt; apiserver-to-kubelet-rbac.yaml &lt;&lt; EOF</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1</span><br><span class="line">kind: ClusterRole</span><br><span class="line">metadata:</span><br><span class="line">  annotations:</span><br><span class="line">    rbac.authorization.kubernetes.io/autoupdate: &quot;true&quot;</span><br><span class="line">  labels:</span><br><span class="line">    kubernetes.io/bootstrapping: rbac-defaults</span><br><span class="line">  name: system:kube-apiserver-to-kubelet</span><br><span class="line">rules:</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - nodes/proxy</span><br><span class="line">      - nodes/stats</span><br><span class="line">      - nodes/log</span><br><span class="line">      - nodes/spec</span><br><span class="line">      - nodes/metrics</span><br><span class="line">      - pods/log</span><br><span class="line">    verbs:</span><br><span class="line">      - &quot;*&quot;</span><br><span class="line">---</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1</span><br><span class="line">kind: ClusterRoleBinding</span><br><span class="line">metadata:</span><br><span class="line">  name: system:kube-apiserver</span><br><span class="line">  namespace: &quot;&quot;</span><br><span class="line">roleRef:</span><br><span class="line">  apiGroup: rbac.authorization.k8s.io</span><br><span class="line">  kind: ClusterRole</span><br><span class="line">  name: system:kube-apiserver-to-kubelet</span><br><span class="line">subjects:</span><br><span class="line">  - apiGroup: rbac.authorization.k8s.io</span><br><span class="line">    kind: User</span><br><span class="line">    name: kubernetes</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ kubectl apply -f apiserver-to-kubelet-rbac.yaml</span><br><span class="line"></span><br><span class="line">clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created</span><br><span class="line"></span><br><span class="line">➜ kubectl -n kube-system logs calico-kube-controllers-85578c44bf-7v87p</span><br></pre></td></tr></table></figure><h3 id="部署-coredns">11.3 部署 coredns</h3><p><a href="https://github.com/coredns/deployment/blob/master/kubernetes/coredns.yaml.sed" class="uri">https://github.com/coredns/deployment/blob/master/kubernetes/coredns.yaml.sed</a></p><p><strong>coredns.yaml.sed 原始文件见附录章节 "16.1 coredns.yaml.sed"，该 yaml 指定使用的 coredns 的版本是 1.9.4。</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载 yaml 文件</span></span><br><span class="line">➜ curl https://raw.kgithub.com/coredns/deployment/master/kubernetes/coredns.yaml.sed -o coredns.yaml</span><br></pre></td></tr></table></figure><p>修改配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">&quot;coredns/coredns:1.9.4&quot;</span> 替换为 <span class="string">&quot;coredns/coredns:1.10.1&quot;</span></span></span><br><span class="line">➜ sed -i &#x27;s/coredns\/coredns:1.9.4/coredns\/coredns:1.10.1/g&#x27; coredns.yaml</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">&quot;CLUSTER_DOMAIN&quot;</span> 替换为 <span class="string">&quot;cluster.local&quot;</span></span></span><br><span class="line">➜ sed -i &#x27;s/CLUSTER_DOMAIN/cluster.local/g&#x27; coredns.yaml</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">&quot;REVERSE_CIDRS&quot;</span> 替换为 <span class="string">&quot;in-addr.arpa ip6.arpa&quot;</span></span></span><br><span class="line">➜ sed -i &#x27;s/REVERSE_CIDRS/in-addr.arpa ip6.arpa/g&#x27; coredns.yaml</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">&quot;UPSTREAMNAMESERVER&quot;</span> 替换为 <span class="string">&quot;/etc/resolv.conf&quot;</span>（或当前网络所使用的 DNS 地址）</span></span><br><span class="line">➜ sed -i &#x27;s/UPSTREAMNAMESERVER/\/etc\/resolv.conf/g&#x27; coredns.yaml</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">&quot;STUBDOMAINS&quot;</span> 替换为 <span class="string">&quot;&quot;</span></span></span><br><span class="line">➜ sed -i &#x27;s/STUBDOMAINS//g&#x27; coredns.yaml</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">&quot;CLUSTER_DNS_IP&quot;</span> 替换为 <span class="string">&quot;10.96.0.10&quot;</span>（与 kubelet.yaml 配置的 clusterDNS 一致）</span></span><br><span class="line">➜ sed -i &#x27;s/CLUSTER_DNS_IP/10.96.0.10/g&#x27; coredns.yaml</span><br></pre></td></tr></table></figure><p>安装：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl apply -f coredns.yaml</span><br><span class="line"></span><br><span class="line">serviceaccount/coredns created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io/system:coredns created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/system:coredns created</span><br><span class="line">configmap/coredns created</span><br><span class="line">deployment.apps/coredns created</span><br><span class="line">service/kube-dns created</span><br></pre></td></tr></table></figure><p>验证<strong>（如果 calico 的 pod 未就绪，请检查是否是镜像拉取未完成或镜像拉取失败）</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl -n kube-system get deploy,pod,svc</span><br><span class="line"></span><br><span class="line">NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE</span><br><span class="line">deployment.apps/calico-kube-controllers   1/1     1            1           23h</span><br><span class="line">deployment.apps/coredns                   1/1     1            1           43s</span><br><span class="line"></span><br><span class="line">NAME                                           READY   STATUS    RESTARTS   AGE</span><br><span class="line">pod/calico-kube-controllers-85578c44bf-7v87p   1/1     Running   0          23h</span><br><span class="line">pod/calico-node-v924z                          1/1     Running   0          23h</span><br><span class="line">pod/coredns-db5667c87-zg9s8                    1/1     Running   0          41s</span><br><span class="line"></span><br><span class="line">NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE</span><br><span class="line">service/kube-dns   ClusterIP   10.96.0.10   &lt;none&gt;        53/UDP,53/TCP,9153/TCP   43s</span><br></pre></td></tr></table></figure><p>dig 测试：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ yum -y install bind-utils</span><br><span class="line"></span><br><span class="line">➜ dig -t A www.baidu.com @10.96.0.10 +short</span><br><span class="line"></span><br><span class="line">www.a.shifen.com.</span><br><span class="line">14.119.104.254</span><br><span class="line">14.119.104.189</span><br></pre></td></tr></table></figure><p>pod 测试：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl run -it --rm --image=busybox:1.28.3 -- sh</span><br><span class="line"></span><br><span class="line">If you don&#x27;t see a command prompt, try pressing enter.</span><br><span class="line">/ # cat /etc/resolv.conf</span><br><span class="line">search default.svc.cluster.local svc.cluster.local cluster.local</span><br><span class="line">nameserver 10.96.0.10</span><br><span class="line">options ndots:5</span><br><span class="line">/ # nslookup kubernetes.default</span><br><span class="line">Server:    10.96.0.10</span><br><span class="line">Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local</span><br><span class="line"></span><br><span class="line">Name:      kubernetes.default</span><br><span class="line">Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local</span><br><span class="line">/ # ping -c 4 www.baidu.com</span><br><span class="line">PING www.baidu.com (14.119.104.254): 56 data bytes</span><br><span class="line">64 bytes from 14.119.104.254: seq=0 ttl=127 time=14.193 ms</span><br><span class="line">64 bytes from 14.119.104.254: seq=1 ttl=127 time=12.848 ms</span><br><span class="line">64 bytes from 14.119.104.254: seq=2 ttl=127 time=18.553 ms</span><br><span class="line">64 bytes from 14.119.104.254: seq=3 ttl=127 time=23.581 ms</span><br><span class="line"></span><br><span class="line">--- www.baidu.com ping statistics ---</span><br><span class="line">4 packets transmitted, 4 packets received, 0% packet loss</span><br><span class="line">round-trip min/avg/max = 12.848/17.293/23.581 ms</span><br></pre></td></tr></table></figure><h2 id="添加-worker-节点">12. 添加 worker 节点</h2><p>worker 节点需要部署两个组件 <code>kubelet</code>, <code>kube-proxy</code>。</p><p><strong>在 master 节点执行，从 master 节点上复制以下文件到 worker 节点：</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">➜ scp /etc/kubernetes/pki/ca.pem \</span><br><span class="line">       /etc/kubernetes/pki/kube-proxy.pem \</span><br><span class="line">       /etc/kubernetes/pki/kube-proxy-key.pem \</span><br><span class="line">       root@worker1:/etc/kubernetes/pki/</span><br><span class="line"></span><br><span class="line">➜ scp /etc/kubernetes/kubelet.conf \</span><br><span class="line">       /etc/kubernetes/kubelet.yaml \</span><br><span class="line">       /etc/kubernetes/kubelet-bootstrap.kubeconfig \</span><br><span class="line">       /etc/kubernetes/kube-proxy.conf \</span><br><span class="line">       /etc/kubernetes/kube-proxy.yaml \</span><br><span class="line">       /etc/kubernetes/kube-proxy.kubeconfig \</span><br><span class="line">       root@worker1:/etc/kubernetes/</span><br><span class="line"></span><br><span class="line">➜ scp /usr/lib/systemd/system/kubelet.service \</span><br><span class="line">       /usr/lib/systemd/system/kube-proxy.service \</span><br><span class="line">       root@worker1:/usr/lib/systemd/system/</span><br><span class="line"></span><br><span class="line">➜ scp /usr/local/bin/kubelet \</span><br><span class="line">       /usr/local/bin/kube-proxy \</span><br><span class="line">       root@worker1:/usr/local/bin/</span><br></pre></td></tr></table></figure><p><strong>在 worker 节点执行，worker 节点启动 kube-proxy 服务：</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-proxy</span><br></pre></td></tr></table></figure><p><strong>在 worker 节点执行，worker 节点启动 kubelet 服务：</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kubelet</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kubelet</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kubelet</span><br></pre></td></tr></table></figure><p>（master 节点执行）批准 worker 节点加入集群</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get csr</span><br><span class="line">NAME        AGE   SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION</span><br><span class="line">csr-9twkl   85s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   &lt;none&gt;              Pending</span><br><span class="line"></span><br><span class="line">➜ kubectl certificate approve csr-9twkl</span><br><span class="line"></span><br><span class="line">certificatesigningrequest.certificates.k8s.io/csr-9twkl approved</span><br><span class="line"></span><br><span class="line">➜ kubectl get csr</span><br><span class="line"></span><br><span class="line">NAME        AGE     SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION</span><br><span class="line">csr-9twkl   2m15s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   &lt;none&gt;              Approved,Issued</span><br></pre></td></tr></table></figure><p><strong>在 master 节点执行，查看节点：</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get node</span><br><span class="line"></span><br><span class="line">NAME      STATUS   ROLES    AGE   VERSION</span><br><span class="line">master1   Ready    &lt;none&gt;   47h   v1.27.3</span><br><span class="line">worker1   Ready    &lt;none&gt;   26m   v1.27.3</span><br></pre></td></tr></table></figure><p><strong>如果 worker1 的状态仍是 NotReady，请检查是否是镜像拉取未完成或镜像拉取失败。</strong></p><h2 id="禁止-master-节点运行-pod">13. 禁止 master 节点运行 pod</h2><p>至此 1 master 1 worker 的 k8s 二进制集群已搭建完毕。</p><p>此外，还可以给节点打上角色标签，使得查看节点信息更加直观：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">给 master 节点打上 master,etcd 角色标签</span></span><br><span class="line">➜ kubectl label node master1 node-role.kubernetes.io/master=true node-role.kubernetes.io/etcd=true</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">给 worker 节点打上 worker 角色标签</span></span><br><span class="line">➜ kubectl label node worker1 node-role.kubernetes.io/worker=true</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看标签</span></span><br><span class="line">➜ kubectl get node --show-labels</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除标签</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ kubectl label node master1 node-role.kubernetes.io/etcd-</span></span><br></pre></td></tr></table></figure><p>如果不希望 master 节点运行 Pod，则给 master 打上污点：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">添加污点</span></span><br><span class="line">➜ kubectl taint node master1 node-role.kubernetes.io/master=true:NoSchedule</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看污点</span></span><br><span class="line">➜ kubectl describe node master1 | grep Taints</span><br><span class="line"></span><br><span class="line">Taints:             node-role.kubernetes.io/master=true:NoSchedule</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看全部节点的污点</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ kubectl get nodes -o jsonpath=<span class="string">&#x27;&#123;range .items[*]&#125;&#123;.metadata.name&#125;&#123;&quot;\n&quot;&#125;&#123;.spec.taints&#125;&#123;&quot;\n\n&quot;&#125;&#123;end&#125;&#x27;</span></span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除污点</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">➜ kubectl taint node master1 node-role.kubernetes.io/master-</span></span><br></pre></td></tr></table></figure><p>后续可以新增 2 个 etcd 节点组成 etcd 集群，新增 2 个控制平面，避免单点故障。</p><h2 id="测试应用服务部署">14. 测试应用服务部署</h2><p>创建 namespace：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl create namespace dev</span><br><span class="line"></span><br><span class="line">namespace/dev created</span><br><span class="line"></span><br><span class="line">➜ kubectl get namespace</span><br><span class="line"></span><br><span class="line">NAME              STATUS   AGE</span><br><span class="line">default           Active   2d</span><br><span class="line">dev               Active   7m</span><br><span class="line">kube-node-lease   Active   2d</span><br><span class="line">kube-public       Active   2d</span><br><span class="line">kube-system       Active   2d</span><br></pre></td></tr></table></figure><p>创建 deployment：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir -p /etc/kubernetes/demo</span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/demo/nginx-deployment.yaml &lt;&lt; EOF</span><br><span class="line">apiVersion: apps/v1</span><br><span class="line">kind: Deployment</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-deployment</span><br><span class="line">  namespace: dev</span><br><span class="line">spec:</span><br><span class="line">  replicas: 1</span><br><span class="line">  selector:</span><br><span class="line">    matchLabels:</span><br><span class="line">      app: nginx-pod</span><br><span class="line">  template:</span><br><span class="line">    metadata:</span><br><span class="line">      labels:</span><br><span class="line">        app: nginx-pod</span><br><span class="line">    spec:</span><br><span class="line">      containers:</span><br><span class="line">      - name: nginx</span><br><span class="line">        image: nginx:latest</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ kubectl apply -f /etc/kubernetes/demo/nginx-deployment.yaml</span><br><span class="line"></span><br><span class="line">deployment.apps/nginx-deployment created</span><br><span class="line"></span><br><span class="line">➜ kubectl -n dev get pod</span><br><span class="line"></span><br><span class="line">NAME                                READY   STATUS    RESTARTS   AGE</span><br><span class="line">nginx-deployment-6d76bcb866-zl52j   1/1     Running   0          23s</span><br></pre></td></tr></table></figure><p>创建 service：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/demo/nginx-service.yaml &lt;&lt; EOF</span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: Service</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-service</span><br><span class="line">  namespace: dev</span><br><span class="line">spec:</span><br><span class="line">  selector:</span><br><span class="line">    app: nginx-pod</span><br><span class="line">  type: NodePort</span><br><span class="line">  ports:</span><br><span class="line">  - port: 80</span><br><span class="line">    targetPort: 80</span><br><span class="line">    nodePort: 30001</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ kubectl apply -f /etc/kubernetes/demo/nginx-service.yaml</span><br><span class="line"></span><br><span class="line">service/nginx-service created</span><br><span class="line"></span><br><span class="line">➜ kubectl -n dev get svc</span><br><span class="line"></span><br><span class="line">NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE</span><br><span class="line">nginx-service   NodePort   10.96.195.221   &lt;none&gt;        80:30001/TCP   13s</span><br></pre></td></tr></table></figure><p>测试服务访问：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ curl 10.128.170.21:30001 -I</span><br><span class="line"></span><br><span class="line">HTTP/1.1 200 OK</span><br><span class="line">Server: nginx/1.25.1</span><br><span class="line">Date: Tue, 18 Jul 2023 16:56:37 GMT</span><br><span class="line">Content-Type: text/html</span><br><span class="line">Content-Length: 615</span><br><span class="line">Last-Modified: Tue, 13 Jun 2023 15:08:10 GMT</span><br><span class="line">Connection: keep-alive</span><br><span class="line">ETag: &quot;6488865a-267&quot;</span><br><span class="line">Accept-Ranges: bytes</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="部署-dashboard">15. 部署 Dashboard</h2><p>在 Kubernetes 社区中，有一个很受欢迎的 Dashboard 项目，它可以给用户提供一个可视化的 Web 界面来查看当前集群的各种信息。用户可以用 Kubernetes Dashboard 部署容器化的应用、监控应用的状态、执行故障排查任务以及管理 Kubernetes 各种资源。</p><p>官方参考文档：</p><ul><li><a href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/" class="uri">https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/</a></li><li><a href="https://github.com/kubernetes/dashboard/tree/v2.7.0" class="uri">https://github.com/kubernetes/dashboard/tree/v2.7.0</a></li></ul><p>使用 nodeport 方式将 dashboard 服务暴露在集群外，指定使用 30443 端口。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载相关 yaml 文件</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">https://github.com/kubernetes/dashboard/blob/v2.7.0/aio/deploy/recommended.yaml</span></span><br><span class="line">➜ curl https://fastly.jsdelivr.net/gh/kubernetes/dashboard@v2.7.0/aio/deploy/recommended.yaml -o kubernetes-dashboard.yaml</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">修改 Service 部分</span></span><br><span class="line">➜ vim kubernetes-dashboard.yaml</span><br><span class="line">kind: Service</span><br><span class="line">apiVersion: v1</span><br><span class="line">metadata:</span><br><span class="line">  labels:</span><br><span class="line">    k8s-app: kubernetes-dashboard</span><br><span class="line">  name: kubernetes-dashboard</span><br><span class="line">  namespace: kubernetes-dashboard</span><br><span class="line">spec:</span><br><span class="line">  type: NodePort  # 新增</span><br><span class="line">  ports:</span><br><span class="line">    - port: 443</span><br><span class="line">      targetPort: 8443</span><br><span class="line">      nodePort: 30443  # 新增</span><br><span class="line">  selector:</span><br><span class="line">    k8s-app: kubernetes-dashboard</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">部署</span></span><br><span class="line">➜ kubectl apply -f kubernetes-dashboard.yaml</span><br><span class="line"></span><br><span class="line">namespace/kubernetes-dashboard created</span><br><span class="line">serviceaccount/kubernetes-dashboard created</span><br><span class="line">service/kubernetes-dashboard created</span><br><span class="line">secret/kubernetes-dashboard-certs created</span><br><span class="line">secret/kubernetes-dashboard-csrf created</span><br><span class="line">secret/kubernetes-dashboard-key-holder created</span><br><span class="line">configmap/kubernetes-dashboard-settings created</span><br><span class="line">role.rbac.authorization.k8s.io/kubernetes-dashboard created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created</span><br><span class="line">rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created</span><br><span class="line">deployment.apps/kubernetes-dashboard created</span><br><span class="line">service/dashboard-metrics-scraper created</span><br><span class="line">deployment.apps/dashboard-metrics-scraper created</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看 kubernetes-dashboard 下的资源</span></span><br><span class="line">➜ kubectl -n kubernetes-dashboard get deploy</span><br><span class="line"></span><br><span class="line">NAME                        READY   UP-TO-DATE   AVAILABLE   AGE</span><br><span class="line">dashboard-metrics-scraper   1/1     1            1           5m26s</span><br><span class="line">kubernetes-dashboard        1/1     1            1           5m28s</span><br><span class="line"></span><br><span class="line">➜ kubectl -n kubernetes-dashboard get pod</span><br><span class="line"></span><br><span class="line">NAME                                         READY   STATUS    RESTARTS   AGE</span><br><span class="line">dashboard-metrics-scraper-5cb4f4bb9c-8qpbc   1/1     Running   0          6m22s</span><br><span class="line">kubernetes-dashboard-6967859bff-fvhkv        1/1     Running   0          6m22s</span><br><span class="line"></span><br><span class="line">➜ kubectl get svc -n kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">NAME                        TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE</span><br><span class="line">dashboard-metrics-scraper   ClusterIP   10.96.6.178   &lt;none&gt;        8000/TCP        6m37s</span><br><span class="line">kubernetes-dashboard        NodePort    10.96.8.70    &lt;none&gt;        443:30443/TCP   6m41s</span><br></pre></td></tr></table></figure><p><strong>如果 kubernetes-dashboard 下的资源一直未就绪，请检查是否是正在拉取镜像或者镜像一直拉取失败。</strong></p><p>例如：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl -n kubernetes-dashboard describe pod kubernetes-dashboard-546cbc58cd-hzvhr</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line">Events:</span><br><span class="line">  Type    Reason     Age    From               Message</span><br><span class="line">  ----    ------     ----   ----               -------</span><br><span class="line">  Normal  Scheduled  6m20s  default-scheduler  Successfully assigned kubernetes-dashboard/kubernetes-dashboard-546cbc58cd-hzvhr to worker1</span><br><span class="line">  Normal  Pulling    6m20s  kubelet            Pulling image &quot;kubernetesui/dashboard:v2.5.0&quot;</span><br><span class="line"></span><br><span class="line">➜ kubectl -n kubernetes-dashboard describe pod kubernetes-dashboard-546cbc58cd-hzvhr</span><br><span class="line"></span><br><span class="line">Events:</span><br><span class="line">  Type     Reason          Age                 From               Message</span><br><span class="line">  ----     ------          ----                ----               -------</span><br><span class="line">  Normal   Scheduled       10m                 default-scheduler  Successfully assigned kubernetes-dashboard/kubernetes-dashboard-546cbc58cd-hzvhr to worker1</span><br><span class="line">  Warning  Failed          2m1s                kubelet            Failed to pull image &quot;kubernetesui/dashboard:v2.5.0&quot;: rpc error: code = Unknown desc = dial tcp 104.18.124.25:443: i/o timeout</span><br><span class="line">  Warning  Failed          2m1s                kubelet            Error: ErrImagePull</span><br><span class="line">  Normal   SandboxChanged  2m                  kubelet            Pod sandbox changed, it will be killed and re-created.</span><br><span class="line">  Normal   BackOff         118s (x3 over 2m)   kubelet            Back-off pulling image &quot;kubernetesui/dashboard:v2.5.0&quot;</span><br><span class="line">  Warning  Failed          118s (x3 over 2m)   kubelet            Error: ImagePullBackOff</span><br><span class="line">  Normal   Pulling         106s (x2 over 10m)  kubelet            Pulling image &quot;kubernetesui/dashboard:v2.5.0&quot;</span><br><span class="line">  Normal   Pulled          25s                 kubelet            Successfully pulled image &quot;kubernetesui/dashboard:v2.5.0&quot; in 1m21.608630166s</span><br><span class="line">  Normal   Created         22s                 kubelet            Created container kubernetes-dashboard</span><br><span class="line">  Normal   Started         21s                 kubelet            Started container kubernetes-dashboard</span><br></pre></td></tr></table></figure><p>创建 service account 并绑定默认 cluster-admin 管理员集群角色：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下面创建了一个叫 admin-user 的服务账号，放在 kubernetes-dashboard 命名空间下，并将 cluster-admin 角色绑定到 admin-user 账户，这样 admin-user 账户就有了管理员的权限。</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">默认情况下，kubeadm 创建集群时已经创建了 cluster-admin 角色，我们直接绑定即可。</span></span><br><span class="line">➜ cat &gt; dashboard-admin-user.yaml &lt;&lt; EOF</span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: ServiceAccount</span><br><span class="line">metadata:</span><br><span class="line">  name: admin-user</span><br><span class="line">  namespace: kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1</span><br><span class="line">kind: ClusterRoleBinding</span><br><span class="line">metadata:</span><br><span class="line">  name: admin-user</span><br><span class="line">roleRef:</span><br><span class="line">  apiGroup: rbac.authorization.k8s.io</span><br><span class="line">  kind: ClusterRole</span><br><span class="line">  name: cluster-admin</span><br><span class="line">subjects:</span><br><span class="line">  - kind: ServiceAccount</span><br><span class="line">    name: admin-user</span><br><span class="line">    namespace: kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">https://github.com/kubernetes/kubernetes/issues/110113<span class="comment">#issuecomment-1130412032</span></span></span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: Secret</span><br><span class="line">type: kubernetes.io/service-account-token</span><br><span class="line">metadata:</span><br><span class="line">  name: admin-user</span><br><span class="line">  namespace: kubernetes-dashboard</span><br><span class="line">  annotations:</span><br><span class="line">    kubernetes.io/service-account.name: admin-user</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">应用资源配置清单</span></span><br><span class="line">➜ kubectl apply -f dashboard-admin-user.yaml</span><br><span class="line"></span><br><span class="line">serviceaccount/admin-user created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/admin-user created</span><br><span class="line">secret/admin-user created</span><br></pre></td></tr></table></figure><p>查看 admin-user 账户的 token：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk &#x27;&#123;print $1&#125;&#x27;)</span><br><span class="line"></span><br><span class="line">Name:         admin-user</span><br><span class="line">Namespace:    kubernetes-dashboard</span><br><span class="line">Labels:       &lt;none&gt;</span><br><span class="line">Annotations:  kubernetes.io/service-account.name: admin-user</span><br><span class="line">              kubernetes.io/service-account.uid: 630430bb-4ea5-4026-81ef-9d4c39089bca</span><br><span class="line"></span><br><span class="line">Type:  kubernetes.io/service-account-token</span><br><span class="line"></span><br><span class="line">Data</span><br><span class="line">====</span><br><span class="line">ca.crt:     1318 bytes</span><br><span class="line">namespace:  20 bytes</span><br><span class="line">token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IlRpS0VJZ0pkRW5va3Bsb2lKOUxVRXVtM3l6RFNtaFNzUkFFLW1zcXBHS2sifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI2MzA0MzBiYi00ZWE1LTQwMjYtODFlZi05ZDRjMzkwODliY2EiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.C-HqHea6OsWhQ9-yjPo0DGLhrgvtQ1cdaXOeGBOZqDKbPU4s-8VO31Ihw9Fbxo6vQnLJUyzFvRVB45eKr_95sJUht1lnD4pZOJHqnvSAa9SzkHbt4FcylHHG723wplLJc3fvnyKr1u3g74hHRUfLAE3q_VghMVwHi6hRyOalYN3KiFzQXKLVyovCxxAGwaEwJg9ftiawYMkDSzxLKkI17BBwrU_zt_xAKrLn229f9eEKsTeBMju0QMyhoWKCSVbV0chfw-sbJSUMAj7a8Ff5-uY1tru-QqUGI6RzlSKlI4E5hpsUVEFuU0HIHzrwxElTmNJnLZtcotFTLrsdHIXj2w</span><br></pre></td></tr></table></figure><p>使用输出的 token 登录 Dashboard：</p><p><a href="https://10.128.170.21:30443" class="uri">https://10.128.170.21:30443</a></p><h2 id="附录">16. 附录</h2><h3 id="coredns.yaml.sed">16.1 coredns.yaml.sed</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ServiceAccount</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">rbac.authorization.k8s.io/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ClusterRole</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">kubernetes.io/bootstrapping:</span> <span class="string">rbac-defaults</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">system:coredns</span></span><br><span class="line"><span class="attr">rules:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">apiGroups:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="attr">resources:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">endpoints</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">services</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">pods</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">namespaces</span></span><br><span class="line">    <span class="attr">verbs:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">list</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">watch</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">apiGroups:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">discovery.k8s.io</span></span><br><span class="line">    <span class="attr">resources:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">endpointslices</span></span><br><span class="line">    <span class="attr">verbs:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">list</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">watch</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">rbac.authorization.k8s.io/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ClusterRoleBinding</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">annotations:</span></span><br><span class="line">    <span class="attr">rbac.authorization.kubernetes.io/autoupdate:</span> <span class="string">&quot;true&quot;</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">kubernetes.io/bootstrapping:</span> <span class="string">rbac-defaults</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">system:coredns</span></span><br><span class="line"><span class="attr">roleRef:</span></span><br><span class="line">  <span class="attr">apiGroup:</span> <span class="string">rbac.authorization.k8s.io</span></span><br><span class="line">  <span class="attr">kind:</span> <span class="string">ClusterRole</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">system:coredns</span></span><br><span class="line"><span class="attr">subjects:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">kind:</span> <span class="string">ServiceAccount</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ConfigMap</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line"><span class="attr">data:</span></span><br><span class="line">  <span class="attr">Corefile:</span> <span class="string">|</span></span><br><span class="line"><span class="string">    .:53 &#123;</span></span><br><span class="line"><span class="string">        errors</span></span><br><span class="line"><span class="string">        health &#123;</span></span><br><span class="line"><span class="string">          lameduck 5s</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">        ready</span></span><br><span class="line"><span class="string">        kubernetes CLUSTER_DOMAIN REVERSE_CIDRS &#123;</span></span><br><span class="line"><span class="string">          fallthrough in-addr.arpa ip6.arpa</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">        prometheus :9153</span></span><br><span class="line"><span class="string">        forward . UPSTREAMNAMESERVER &#123;</span></span><br><span class="line"><span class="string">          max_concurrent 1000</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">        cache 30</span></span><br><span class="line"><span class="string">        loop</span></span><br><span class="line"><span class="string">        reload</span></span><br><span class="line"><span class="string">        loadbalance</span></span><br><span class="line"><span class="string">    &#125;STUBDOMAINS</span></span><br><span class="line"><span class="string"></span><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">apps/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Deployment</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">    <span class="attr">kubernetes.io/name:</span> <span class="string">&quot;CoreDNS&quot;</span></span><br><span class="line">    <span class="attr">app.kubernetes.io/name:</span> <span class="string">coredns</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="comment"># replicas: not specified here:</span></span><br><span class="line">  <span class="comment"># 1. Default is 1.</span></span><br><span class="line">  <span class="comment"># 2. Will be tuned in real time if DNS horizontal auto-scaling is turned on.</span></span><br><span class="line">  <span class="attr">strategy:</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">RollingUpdate</span></span><br><span class="line">    <span class="attr">rollingUpdate:</span></span><br><span class="line">      <span class="attr">maxUnavailable:</span> <span class="number">1</span></span><br><span class="line">  <span class="attr">selector:</span></span><br><span class="line">    <span class="attr">matchLabels:</span></span><br><span class="line">      <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">      <span class="attr">app.kubernetes.io/name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">template:</span></span><br><span class="line">    <span class="attr">metadata:</span></span><br><span class="line">      <span class="attr">labels:</span></span><br><span class="line">        <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">        <span class="attr">app.kubernetes.io/name:</span> <span class="string">coredns</span></span><br><span class="line">    <span class="attr">spec:</span></span><br><span class="line">      <span class="attr">priorityClassName:</span> <span class="string">system-cluster-critical</span></span><br><span class="line">      <span class="attr">serviceAccountName:</span> <span class="string">coredns</span></span><br><span class="line">      <span class="attr">tolerations:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">key:</span> <span class="string">&quot;CriticalAddonsOnly&quot;</span></span><br><span class="line">          <span class="attr">operator:</span> <span class="string">&quot;Exists&quot;</span></span><br><span class="line">      <span class="attr">nodeSelector:</span></span><br><span class="line">        <span class="attr">kubernetes.io/os:</span> <span class="string">linux</span></span><br><span class="line">      <span class="attr">affinity:</span></span><br><span class="line">         <span class="attr">podAntiAffinity:</span></span><br><span class="line">           <span class="attr">requiredDuringSchedulingIgnoredDuringExecution:</span></span><br><span class="line">           <span class="bullet">-</span> <span class="attr">labelSelector:</span></span><br><span class="line">               <span class="attr">matchExpressions:</span></span><br><span class="line">               <span class="bullet">-</span> <span class="attr">key:</span> <span class="string">k8s-app</span></span><br><span class="line">                 <span class="attr">operator:</span> <span class="string">In</span></span><br><span class="line">                 <span class="attr">values:</span> [<span class="string">&quot;kube-dns&quot;</span>]</span><br><span class="line">             <span class="attr">topologyKey:</span> <span class="string">kubernetes.io/hostname</span></span><br><span class="line">      <span class="attr">containers:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">        <span class="attr">image:</span> <span class="string">coredns/coredns:1.9.4</span></span><br><span class="line">        <span class="attr">imagePullPolicy:</span> <span class="string">IfNotPresent</span></span><br><span class="line">        <span class="attr">resources:</span></span><br><span class="line">          <span class="attr">limits:</span></span><br><span class="line">            <span class="attr">memory:</span> <span class="string">170Mi</span></span><br><span class="line">          <span class="attr">requests:</span></span><br><span class="line">            <span class="attr">cpu:</span> <span class="string">100m</span></span><br><span class="line">            <span class="attr">memory:</span> <span class="string">70Mi</span></span><br><span class="line">        <span class="attr">args:</span> [ <span class="string">&quot;-conf&quot;</span>, <span class="string">&quot;/etc/coredns/Corefile&quot;</span> ]</span><br><span class="line">        <span class="attr">volumeMounts:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">config-volume</span></span><br><span class="line">          <span class="attr">mountPath:</span> <span class="string">/etc/coredns</span></span><br><span class="line">          <span class="attr">readOnly:</span> <span class="literal">true</span></span><br><span class="line">        <span class="attr">ports:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">containerPort:</span> <span class="number">53</span></span><br><span class="line">          <span class="attr">name:</span> <span class="string">dns</span></span><br><span class="line">          <span class="attr">protocol:</span> <span class="string">UDP</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">containerPort:</span> <span class="number">53</span></span><br><span class="line">          <span class="attr">name:</span> <span class="string">dns-tcp</span></span><br><span class="line">          <span class="attr">protocol:</span> <span class="string">TCP</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">containerPort:</span> <span class="number">9153</span></span><br><span class="line">          <span class="attr">name:</span> <span class="string">metrics</span></span><br><span class="line">          <span class="attr">protocol:</span> <span class="string">TCP</span></span><br><span class="line">        <span class="attr">securityContext:</span></span><br><span class="line">          <span class="attr">allowPrivilegeEscalation:</span> <span class="literal">false</span></span><br><span class="line">          <span class="attr">capabilities:</span></span><br><span class="line">            <span class="attr">add:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">NET_BIND_SERVICE</span></span><br><span class="line">            <span class="attr">drop:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">all</span></span><br><span class="line">          <span class="attr">readOnlyRootFilesystem:</span> <span class="literal">true</span></span><br><span class="line">        <span class="attr">livenessProbe:</span></span><br><span class="line">          <span class="attr">httpGet:</span></span><br><span class="line">            <span class="attr">path:</span> <span class="string">/health</span></span><br><span class="line">            <span class="attr">port:</span> <span class="number">8080</span></span><br><span class="line">            <span class="attr">scheme:</span> <span class="string">HTTP</span></span><br><span class="line">          <span class="attr">initialDelaySeconds:</span> <span class="number">60</span></span><br><span class="line">          <span class="attr">timeoutSeconds:</span> <span class="number">5</span></span><br><span class="line">          <span class="attr">successThreshold:</span> <span class="number">1</span></span><br><span class="line">          <span class="attr">failureThreshold:</span> <span class="number">5</span></span><br><span class="line">        <span class="attr">readinessProbe:</span></span><br><span class="line">          <span class="attr">httpGet:</span></span><br><span class="line">            <span class="attr">path:</span> <span class="string">/ready</span></span><br><span class="line">            <span class="attr">port:</span> <span class="number">8181</span></span><br><span class="line">            <span class="attr">scheme:</span> <span class="string">HTTP</span></span><br><span class="line">      <span class="attr">dnsPolicy:</span> <span class="string">Default</span></span><br><span class="line">      <span class="attr">volumes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">config-volume</span></span><br><span class="line">          <span class="attr">configMap:</span></span><br><span class="line">            <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">            <span class="attr">items:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="attr">key:</span> <span class="string">Corefile</span></span><br><span class="line">              <span class="attr">path:</span> <span class="string">Corefile</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Service</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">kube-dns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line">  <span class="attr">annotations:</span></span><br><span class="line">    <span class="attr">prometheus.io/port:</span> <span class="string">&quot;9153&quot;</span></span><br><span class="line">    <span class="attr">prometheus.io/scrape:</span> <span class="string">&quot;true&quot;</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">    <span class="attr">kubernetes.io/cluster-service:</span> <span class="string">&quot;true&quot;</span></span><br><span class="line">    <span class="attr">kubernetes.io/name:</span> <span class="string">&quot;CoreDNS&quot;</span></span><br><span class="line">    <span class="attr">app.kubernetes.io/name:</span> <span class="string">coredns</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">selector:</span></span><br><span class="line">    <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">    <span class="attr">app.kubernetes.io/name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">clusterIP:</span> <span class="string">CLUSTER_DNS_IP</span></span><br><span class="line">  <span class="attr">ports:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">dns</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">53</span></span><br><span class="line">    <span class="attr">protocol:</span> <span class="string">UDP</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">dns-tcp</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">53</span></span><br><span class="line">    <span class="attr">protocol:</span> <span class="string">TCP</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">metrics</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">9153</span></span><br><span class="line">    <span class="attr">protocol:</span> <span class="string">TCP</span></span><br></pre></td></tr></table></figure><h3 id="helm-安装-coredns">16.2 helm 安装 coredns</h3><p><a href="https://github.com/coredns/helm" class="uri">https://github.com/coredns/helm</a></p><p>安装 helm：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">➜ cd ~/Downloads</span><br><span class="line">➜ curl -O https://mirrors.huaweicloud.com/helm/v3.12.2/helm-v3.12.2-linux-amd64.tar.gz</span><br><span class="line">➜ tar -zxvf helm-v3.12.2-linux-amd64.tar.gz</span><br><span class="line">➜ cp linux-amd64/helm /usr/local/bin</span><br></pre></td></tr></table></figure><p>添加 chart 仓库：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ helm repo add coredns https://coredns.github.io/helm</span><br></pre></td></tr></table></figure><p>查看可安装的版本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ helm search repo -l</span><br><span class="line"></span><br><span class="line">NAME            CHART VERSION   APP VERSION     DESCRIPTION</span><br><span class="line">coredns/coredns 1.24.1          1.10.1          CoreDNS is a DNS server that chains plugins and...</span><br><span class="line">coredns/coredns 1.24.0          1.10.1          CoreDNS is a DNS server that chains plugins and...</span><br><span class="line">coredns/coredns 1.23.0          1.10.1          CoreDNS is a DNS server that chains plugins and...</span><br><span class="line">coredns/coredns 1.22.0          1.10.1          CoreDNS is a DNS server that chains plugins and...</span><br><span class="line">coredns/coredns 1.21.0          1.10.1          CoreDNS is a DNS server that chains plugins and...</span><br><span class="line">coredns/coredns 1.20.2          1.9.4           CoreDNS is a DNS server that chains plugins and...</span><br><span class="line">coredns/coredns 1.20.1          1.9.4           CoreDNS is a DNS server that chains plugins and...</span><br><span class="line">coredns/coredns 1.20.0          1.9.4           CoreDNS is a DNS server that chains plugins and...</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>安装 coredns：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ helm --namespace=kube-system install coredns coredns/coredns</span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://blog.csdn.net/it_zrs/article/details/126622431">K8s 高可用集群架构（二进制）部署及应用</a></p><p><a href="https://www.haxi.cc/archives/setup-k8s-1-23-1-cluster-using-binary.html">二进制部署 k8s 集群 1.23.1 版本</a></p><p><a href="https://hebye.com/docs/k8s/k8s-1ct8ioki6h2qk#2nop09">部署一套完整的企业级k8s集群</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;通过本文的指导，读者可以了解如何通过二进制的方式部署 Kubernetes 1.27.3 版本集群。二进制部署可以加深对 Kubernetes 各组件的理解，可以灵活地将各个组件部署到不同的机器，以满足自身的要求。但是需要注意的是，二进制部署需要手动配置各个组件，需要一定的技术水平和经验。&lt;/p&gt;</summary>
    
    
    
    <category term="cloud" scheme="https://www.wylu.me/categories/cloud/"/>
    
    <category term="kubernetes" scheme="https://www.wylu.me/categories/cloud/kubernetes/"/>
    
    
    <category term="kubernetes" scheme="https://www.wylu.me/tags/kubernetes/"/>
    
  </entry>
  
  <entry>
    <title>Rocky Linux 部署 etcd 集群</title>
    <link href="https://www.wylu.me/posts/aff5958d/"/>
    <id>https://www.wylu.me/posts/aff5958d/</id>
    <published>2023-08-03T16:00:50.000Z</published>
    <updated>2023-08-03T16:21:24.076Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了在 Rocky Linux 系统上部署 etcd 集群的步骤。首先介绍了环境准备，包括三个节点的操作系统、IP地址和版本等信息。接下来，安装必要软件，包括 etcd、证书工具 cfssl 等，并创建 etcd 所需的证书。然后配置 etcd，包括主节点和启动配置，以及其余节点的配置。启动 etcd 服务并验证其状态，包括检查集群健康状态、查看集群节点列表、验证集群数据存储服务和监测数据变化等。此外，还介绍了数据备份与恢复、节点宕机与恢复、ETCD 故障节点修复和身份验证等内容。最后，介绍了如何安装 etcdkeeper。</p><span id="more"></span><h1 id="rocky-linux-部署-etcd-集群">Rocky Linux 部署 etcd 集群</h1><h2 id="环境准备">1. 环境准备</h2><table><thead><tr class="header"><th>主机名</th><th>操作系统</th><th>IP地址</th><th>版本</th></tr></thead><tbody><tr class="odd"><td>node1</td><td>Rocky Linux release 8.6 (Green Obsidian)</td><td>10.128.170.131</td><td>etcd-v3.5.6</td></tr><tr class="even"><td>node2</td><td>Rocky Linux release 8.6 (Green Obsidian)</td><td>10.128.170.132</td><td>etcd-v3.5.6</td></tr><tr class="odd"><td>node3</td><td>Rocky Linux release 8.6 (Green Obsidian)</td><td>10.128.170.133</td><td>etcd-v3.5.6</td></tr></tbody></table><ul><li><p>/etc/hosts 配置</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">10.128.170.131 node1 node1.local</span><br><span class="line">10.128.170.132 node2 node2.local</span><br><span class="line">10.128.170.133 node3 node3.local</span><br></pre></td></tr></table></figure></li></ul><h2 id="安装必要软件">2. 安装必要软件</h2><p><strong>以下必要软件安装操作需要在全部节点执行。</strong></p><p>etcd 集群部署过程中需要用到以下组件：</p><ul><li>cfssl</li><li>cfssljson</li><li>cfssl_certinfo</li><li>etcd</li><li>etcdctl</li></ul><h3 id="创建数据目录">2.1. 创建数据目录</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p /data/etcd/&#123;default,cfg,ssl&#125;</span><br></pre></td></tr></table></figure><ul><li>default 存放 etcd 数据</li><li>cfg 存放 etcd 配置文件</li><li>ssl 存放证书、私钥</li></ul><h3 id="下载安装-etcd">2.2. 下载安装 etcd</h3><p>到 <a href="https://github.com/etcd-io/etcd/releases/tag/v3.5.6" class="uri">https://github.com/etcd-io/etcd/releases/tag/v3.5.6</a> 下载以下文件：</p><ul><li>etcd-v3.5.6-linux-amd64.tar.gz</li></ul><p>解压安装：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -zxvf etcd-v3.5.6-linux-amd64.tar.gz -C /opt</span><br></pre></td></tr></table></figure><p>创建 bin 目录，并将可执行程序移动到 bin 目录下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd /opt/etcd-v3.5.6-linux-amd64</span><br><span class="line">mkdir bin</span><br><span class="line">mv etcd etcdctl etcdutl bin</span><br></pre></td></tr></table></figure><h3 id="下载安装证书工具-cfssl">2.3. 下载安装证书工具 cfssl</h3><p>到 <a href="https://github.com/cloudflare/cfssl/releases/tag/v1.6.3" class="uri">https://github.com/cloudflare/cfssl/releases/tag/v1.6.3</a> 下载以下文件：</p><ul><li>cfssl_1.6.3_linux_amd64</li><li>cfssljson_1.6.3_linux_amd64</li><li>cfssl-certinfo_1.6.3_linux_amd64</li></ul><p>下载证书工具 cfssl 后，移动到 /opt/etcd-v3.5.6-linux-amd64/bin 目录下，并授予可执行权限：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">chmod +x cfssl_1.6.3_linux_amd64</span><br><span class="line">cp cfssl_1.6.3_linux_amd64 /opt/etcd-v3.5.6-linux-amd64/bin/cfssl</span><br><span class="line"></span><br><span class="line">chmod +x cfssljson_1.6.3_linux_amd64</span><br><span class="line">cp cfssljson_1.6.3_linux_amd64 /opt/etcd-v3.5.6-linux-amd64/bin/cfssljson</span><br><span class="line"></span><br><span class="line">chmod +x cfssl-certinfo_1.6.3_linux_amd64</span><br><span class="line">cp cfssl-certinfo_1.6.3_linux_amd64 /opt/etcd-v3.5.6-linux-amd64/bin/cfssl-certinfo</span><br></pre></td></tr></table></figure><p>方便起见，以上工具分别重命名为：</p><ul><li>cfssl</li><li>cfssljson</li><li>cfssl-certinfo</li></ul><h3 id="配置环境变量">2.4. 配置环境变量</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim ~/.bashrc</span><br></pre></td></tr></table></figure><p>添加如下内容：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Etcd Environment</span></span><br><span class="line">export ETCD_HOME=/opt/etcd-v3.5.6-linux-amd64</span><br><span class="line"></span><br><span class="line">export PATH=$PATH:$ETCD_HOME/bin</span><br></pre></td></tr></table></figure><p>使配置立即生效：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure><h2 id="创建-tls-证书">3. 创建 TLS 证书</h2><p><strong>以下创建 TLS 证书操作在任意一个节点执行即可。</strong></p><h3 id="新建-ca-签名请求文件">3.1. 新建 CA 签名请求文件</h3><p>创建 CA 签名请求文件 ca-csr.json，存放在 /data/etcd/ssl 中，文件内容如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">cat &gt; /data/etcd/ssl/ca-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;etcd&quot;,</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;Shenzhen&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>术语介绍：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">C: Country， 国家</span><br><span class="line">L: Locality，地区，城市</span><br><span class="line">O: Organization Name，组织名称，公司名称</span><br><span class="line">OU: Organization Unit Name，组织单位名称，公司部门</span><br><span class="line">ST: State，州，省</span><br></pre></td></tr></table></figure><h3 id="生成-ca-凭证私钥和证书签名请求">3.2. 生成 CA 凭证、私钥和证书签名请求</h3><p>在 /data/etcd/ssl 目录下执行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cfssl gencert -initca ca-csr.json | cfssljson -bare ca</span><br></pre></td></tr></table></figure><p>生成以下三个文件：</p><ul><li>ca-key.pem（CA 私钥）</li><li>ca.pem（CA 证书）</li><li>ca.csr（CA 证书签名请求）</li></ul><h3 id="ca-配置">3.3. CA 配置</h3><p>创建 CA 配置文件 ca-config.json，存放在 /data/etcd/ssl 中，文件内容如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">cat &gt; /data/etcd/ssl/ca-config.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;signing&quot;: &#123;</span><br><span class="line">        &quot;default&quot;: &#123;</span><br><span class="line">            &quot;expiry&quot;: &quot;87600h&quot;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;profiles&quot;: &#123;</span><br><span class="line">            &quot;etcd&quot;: &#123;</span><br><span class="line">                &quot;expiry&quot;: &quot;87600h&quot;,</span><br><span class="line">                &quot;usages&quot;: [</span><br><span class="line">                    &quot;signing&quot;,</span><br><span class="line">                    &quot;key encipherment&quot;,</span><br><span class="line">                    &quot;server auth&quot;,</span><br><span class="line">                    &quot;client auth&quot;</span><br><span class="line">                ]</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>术语介绍：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">profiles：可以设置多个 profile，这里的 profile 是 etcd</span><br><span class="line">expiry：指定了证书的有效期是 10 年(87600h)</span><br><span class="line">signing：表示该证书可用于签名其它证书，生成的 ca.pem 证书中 CA=TRUE</span><br><span class="line">server auth：表示 client 可以用该 CA 对 server 提供的证书进行验证</span><br><span class="line">client auth：表示 server 可以用该 CA 对 client 提供的证书进行验证</span><br></pre></td></tr></table></figure><h3 id="新建-etcd-证书请求文件">3.4. 新建 etcd 证书请求文件</h3><p>新建 etcd 证书请求文件 server-csr.json，存放在 /data/etcd/ssl 中，文件内容如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">cat &gt; /data/etcd/ssl/server-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;server&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.131&quot;,</span><br><span class="line">        &quot;10.128.170.132&quot;,</span><br><span class="line">        &quot;10.128.170.133&quot;,</span><br><span class="line">        &quot;node1&quot;,</span><br><span class="line">        &quot;node2&quot;,</span><br><span class="line">        &quot;node3&quot;,</span><br><span class="line">        &quot;node1.local&quot;,</span><br><span class="line">        &quot;node2.local&quot;,</span><br><span class="line">        &quot;node3.local&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;Shenzhen&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>hosts 字段指定授权使用该证书的 etcd 节点 IP 或域名列表，需要将 etcd 集群的三个节点 IP 都列在其中。</p><h3 id="生成-server-证书">3.5. 生成 server 证书</h3><p>在 /data/etcd/ssl 目录下执行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=etcd server-csr.json | cfssljson -bare server</span><br></pre></td></tr></table></figure><p>生成以下三个文件：</p><ul><li>server-key.pem（server 私钥）</li><li>server.pem（server 证书）</li><li>server.csr（server 证书签名请求）</li></ul><h3 id="拷贝证书到其它节点">3.6. 拷贝证书到其它节点</h3><p>根据需要拷贝 /data/etcd/ssl 目录下的证书文件到其他节点：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd /data/etcd/ssl</span><br><span class="line">scp ca.pem server.pem server-key.pem root@node2:/data/etcd/ssl</span><br><span class="line">scp ca.pem server.pem server-key.pem root@node3:/data/etcd/ssl</span><br></pre></td></tr></table></figure><h2 id="etcd-配置">4. etcd 配置</h2><h3 id="etcd-主节点配置">4.1. etcd 主节点配置</h3><p>创建 etcd 主节点配置文件 etcd.conf，存放在 /data/etcd/cfg 中，文件内容如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">cat &gt; /data/etcd/cfg/etcd.conf &lt;&lt; &quot;EOF&quot;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[Member]</span></span><br><span class="line">ETCD_NAME=&quot;etcd01&quot;</span><br><span class="line">ETCD_DATA_DIR=&quot;/data/etcd/default&quot;</span><br><span class="line">ETCD_LISTEN_PEER_URLS=&quot;https://10.128.170.131:2380&quot;</span><br><span class="line">ETCD_LISTEN_CLIENT_URLS=&quot;https://10.128.170.131:2379,https://127.0.0.1:2379&quot;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[Clustering]</span></span><br><span class="line">ETCD_INITIAL_ADVERTISE_PEER_URLS=&quot;https://10.128.170.131:2380&quot;</span><br><span class="line">ETCD_ADVERTISE_CLIENT_URLS=&quot;https://10.128.170.131:2379&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER=&quot;etcd01=https://10.128.170.131:2380,etcd02=https://10.128.170.132:2380,etcd03=https://10.128.170.133:2380&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER_TOKEN=&quot;etcd-cluster&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER_STATE=&quot;new&quot;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[Security]</span></span><br><span class="line">ETCD_CERT_FILE=&quot;/data/etcd/ssl/server.pem&quot;</span><br><span class="line">ETCD_KEY_FILE=&quot;/data/etcd/ssl/server-key.pem&quot;</span><br><span class="line">ETCD_TRUSTED_CA_FILE=&quot;/data/etcd/ssl/ca.pem&quot;</span><br><span class="line">ETCD_CLIENT_CERT_AUTH=&quot;false&quot;</span><br><span class="line">ETCD_PEER_CERT_FILE=&quot;/data/etcd/ssl/server.pem&quot;</span><br><span class="line">ETCD_PEER_KEY_FILE=&quot;/data/etcd/ssl/server-key.pem&quot;</span><br><span class="line">ETCD_PEER_TRUSTED_CA_FILE=&quot;/data/etcd/ssl/ca.pem&quot;</span><br><span class="line">ETCD_PEER_CLIENT_CERT_AUTH=&quot;true&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><h3 id="etcd-启动配置">4.2. etcd 启动配置</h3><p>创建 etcd 启动配置文件 etcd.service，存放在 /usr/lib/systemd/system 目录下，文件内容如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">cat &gt; /usr/lib/systemd/system/etcd.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Etcd Server</span><br><span class="line">After=network.target</span><br><span class="line">After=network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=notify</span><br><span class="line">EnvironmentFile=/data/etcd/cfg/etcd.conf</span><br><span class="line">ExecStart=/opt/etcd-v3.5.6-linux-amd64/bin/etcd</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">ExecStart=/opt/etcd-v3.5.6-linux-amd64/bin/etcd \</span></span><br><span class="line"><span class="language-bash"><span class="comment">#--name=$&#123;ETCD_NAME&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--data-dir=$&#123;ETCD_DATA_DIR&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--listen-peer-urls=$&#123;ETCD_LISTEN_PEER_URLS&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--listen-client-urls=$&#123;ETCD_LISTEN_CLIENT_URLS&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--advertise-client-urls=$&#123;ETCD_ADVERTISE_CLIENT_URLS&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--initial-advertise-peer-urls=$&#123;ETCD_INITIAL_ADVERTISE_PEER_URLS&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--initial-cluster=$&#123;ETCD_INITIAL_CLUSTER&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--initial-cluster-token=$&#123;ETCD_INITIAL_CLUSTER_TOKEN&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--initial-cluster-state=$&#123;ETCD_INITIAL_CLUSTER_STATE&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--cert-file=$&#123;ETCD_CERT_FILE&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--key-file=$&#123;ETCD_KEY_FILE&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--peer-cert-file=$&#123;ETCD_PEER_CERT_FILE&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--peer-key-file=$&#123;ETCD_PEER_KEY_FILE&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--trusted-ca-file=$&#123;ETCD_TRUSTED_CA_FILE&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--client-cert-auth=$&#123;ETCD_CLIENT_CERT_AUTH&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--peer-client-cert-auth=$&#123;ETCD_PEER_CLIENT_CERT_AUTH&#125; \</span></span></span><br><span class="line"><span class="language-bash"><span class="comment">#--peer-trusted-ca-file=$&#123;ETCD_PEER_TRUSTED_CA_FILE&#125;</span></span></span><br><span class="line">Restart=on-failure</span><br><span class="line">LimitNOFILE=65536</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>字段解释：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">--WorkingDirectory、--data-dir：指定工作目录和数据目录为 $&#123;ETCD_DATA_DIR&#125;，需在启动服务前创建这个目录；</span><br><span class="line">--wal-dir：指定 wal 目录，为了提高性能，一般使用 SSD 或者和 --data-dir 不同的磁盘；</span><br><span class="line">--name：指定节点名称，当 --initial-cluster-state 值为 new 时，--name 的参数值必须位于 --initial-cluster 列表中；</span><br><span class="line">--cert-file、--key-file：etcd server 与 client 通信时使用的证书和私钥；</span><br><span class="line">--trusted-ca-file：签名 client 证书的 CA 证书，用于验证 client 证书；</span><br><span class="line">--peer-cert-file、--peer-key-file：etcd 与 peer 通信使用的证书和私钥；</span><br><span class="line">--peer-trusted-ca-file：签名 peer 证书的 CA 证书，用于验证 peer 证书；</span><br></pre></td></tr></table></figure><blockquote><p>启动异常：conflicting environment variable "ETCD_NAME" is shadowed by corresponding command-line flag (either unset environment variable or disable flag)</p><p>原因分析：ETCD3.4 版本会自动读取环境变量的参数，所以 EnvironmentFile 文件中有的参数，不需要再次在 ExecStart 启动参数中添加，二选一，如同时配置，会触发以上报错。</p><p>解决方法：剔除 ExecStart 中和配置文件重复的内容即可。</p></blockquote><h2 id="配置其余节点">5. 配置其余节点</h2><p>同样操作配置其余节点，但需要修改 etcd.conf 文件中的节点名字以及服务器 IP 地址。</p><h2 id="启动-etcd-服务">6. 启动 etcd 服务</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl start etcd.service</span><br></pre></td></tr></table></figure><h2 id="验证-etcd-服务">7. 验证 etcd 服务</h2><p>在此之前，我们需要设置 etcd 的 API 版本，目前有 v2 与 v3 两个版本，两个版本的 API 存储方式不同，命令工具也不同。我们统一约定使用 v3 版本（默认是 v2 版本），把 v3 加入环境变量中：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">echo &#x27;export ETCDCTL_API=3&#x27; &gt;&gt; ~/.bashrc</span><br><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="检查集群健康状态">7.1. 检查集群健康状态</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; endpoint health</span><br></pre></td></tr></table></figure><p>集群健康则显示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">https://node3.local:2379 is healthy: successfully committed proposal: took = 23.967506ms</span><br><span class="line">https://node1.local:2379 is healthy: successfully committed proposal: took = 36.438089ms</span><br><span class="line">https://node2.local:2379 is healthy: successfully committed proposal: took = 36.013216ms</span><br></pre></td></tr></table></figure><h3 id="查看集群节点列表">7.2. 查看集群节点列表</h3><p>true 代表 leader，由其负责处理客户端请求信息</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; endpoint status</span><br></pre></td></tr></table></figure><p>服务正常则显示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">https://node1.local:2379, 2cc5cd20ac23bb53, 3.5.6, 20 kB, true, false, 2, 11, 11,</span><br><span class="line">https://node2.local:2379, 60084d96d6d6e98a, 3.5.6, 20 kB, false, false, 2, 11, 11,</span><br><span class="line">https://node3.local:2379, 57107ffa28aa353e, 3.5.6, 20 kB, false, false, 2, 11, 11,</span><br></pre></td></tr></table></figure><h3 id="验证集群数据存储服务">7.3. 验证集群数据存储服务</h3><p>任选一个节点，客户端 etcdctl 存储一个数据，其他节点查看数据：</p><p>etcd01 客户端存储数据:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; put /hello &quot;world&quot;</span><br><span class="line">OK</span><br></pre></td></tr></table></figure><p>etcd02 或 etcd03 查看数据：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; get /hello</span><br><span class="line">/hello</span><br><span class="line">world</span><br></pre></td></tr></table></figure><h3 id="监测数据变化">7.4. 监测数据变化</h3><p>使用 watch 命令后，如果一切正常则会进入阻塞，监测一个键值的变化，一旦键值发生更新，就会输出最新的值，直到用户按 CTRL+C 退出 。</p><p>在 10.128.170.133 上一直监控：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; watch /name</span><br></pre></td></tr></table></figure><p>在10.128.170.132 上修改两次 /name 的值：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@node2 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; put /name &quot;first&quot;</span><br><span class="line">OK</span><br><span class="line">[root@node2 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; put /name &quot;second&quot;</span><br><span class="line">OK</span><br></pre></td></tr></table></figure><p>10.128.170.133 监控界面便打印出修改记录：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; watch /name</span><br><span class="line">PUT</span><br><span class="line">/name</span><br><span class="line">first</span><br><span class="line">PUT</span><br><span class="line">/name</span><br><span class="line">second</span><br></pre></td></tr></table></figure><h2 id="数据备份与恢复">8. 数据备份与恢复</h2><h3 id="数据备份">8.1. 数据备份</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379&quot; snapshot save mysnapshot.db</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-01-06T01:01:59.039+0800&quot;,&quot;caller&quot;:&quot;snapshot/v3_snapshot.go:65&quot;,&quot;msg&quot;:&quot;created temporary db file&quot;,&quot;path&quot;:&quot;mysnapshot.db.part&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-01-06T01:01:59.053+0800&quot;,&quot;logger&quot;:&quot;client&quot;,&quot;caller&quot;:&quot;v3@v3.5.6/maintenance.go:212&quot;,&quot;msg&quot;:&quot;opened snapshot stream; downloading&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-01-06T01:01:59.053+0800&quot;,&quot;caller&quot;:&quot;snapshot/v3_snapshot.go:73&quot;,&quot;msg&quot;:&quot;fetching snapshot&quot;,&quot;endpoint&quot;:&quot;https://node1.local:2379&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-01-06T01:01:59.082+0800&quot;,&quot;logger&quot;:&quot;client&quot;,&quot;caller&quot;:&quot;v3@v3.5.6/maintenance.go:220&quot;,&quot;msg&quot;:&quot;completed snapshot read; closing&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-01-06T01:01:59.146+0800&quot;,&quot;caller&quot;:&quot;snapshot/v3_snapshot.go:88&quot;,&quot;msg&quot;:&quot;fetched snapshot&quot;,&quot;endpoint&quot;:&quot;https://node1.local:2379&quot;,&quot;size&quot;:&quot;20 kB&quot;,&quot;took&quot;:&quot;now&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-01-06T01:01:59.146+0800&quot;,&quot;caller&quot;:&quot;snapshot/v3_snapshot.go:97&quot;,&quot;msg&quot;:&quot;saved&quot;,&quot;path&quot;:&quot;mysnapshot.db&quot;&#125;</span><br><span class="line">Snapshot saved at mysnapshot.db</span><br></pre></td></tr></table></figure><h3 id="数据恢复">8.2. 数据恢复</h3><p><a href="https://etcd.io/docs/v3.5/op-guide/recovery/" class="uri">https://etcd.io/docs/v3.5/op-guide/recovery/</a></p><h2 id="节点宕机与恢复">9. 节点宕机与恢复</h2><h3 id="查看集群各节点状态">9.1. 查看集群各节点状态</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; endpoint status --write-out=table</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br><span class="line">|         ENDPOINT         |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br><span class="line">| https://node1.local:2379 | 2cc5cd20ac23bb53 |   3.5.6 |   20 kB |      true |      false |         2 |         14 |                 14 |        |</span><br><span class="line">| https://node2.local:2379 | 60084d96d6d6e98a |   3.5.6 |   20 kB |     false |      false |         2 |         14 |                 14 |        |</span><br><span class="line">| https://node3.local:2379 | 57107ffa28aa353e |   3.5.6 |   20 kB |     false |      false |         2 |         14 |                 14 |        |</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br></pre></td></tr></table></figure><h3 id="模拟主节点-node1-宕机">9.2. 模拟主节点 node1 宕机</h3><p>使用命令 systemctl stop etcd 停掉 leader 节点，再次查看集群状况，可见重新选举出了 leader，集群可正常使用：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# systemctl stop etcd.service</span><br><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; endpoint status --write-out=table</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-01-06T12:39:07.738+0800&quot;,&quot;logger&quot;:&quot;etcd-client&quot;,&quot;caller&quot;:&quot;v3@v3.5.6/retry_interceptor.go:62&quot;,&quot;msg&quot;:&quot;retrying of unary invoker failed&quot;,&quot;target&quot;:&quot;etcd-endpoints://0xc0000b8c40/node1.local:2379&quot;,&quot;attempt&quot;:0,&quot;error&quot;:&quot;rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error: desc = \&quot;transport: Error while dialing dial tcp 10.128.170.131:2379: connect: connection refused\&quot;&quot;&#125;</span><br><span class="line">Failed to get the status of endpoint https://node1.local:2379 (context deadline exceeded)</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br><span class="line">|         ENDPOINT         |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br><span class="line">| https://node2.local:2379 | 60084d96d6d6e98a |   3.5.6 |   20 kB |      true |      false |         3 |         15 |                 15 |        |</span><br><span class="line">| https://node3.local:2379 | 57107ffa28aa353e |   3.5.6 |   20 kB |     false |      false |         3 |         15 |                 15 |        |</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br></pre></td></tr></table></figure><h3 id="模拟从节点-node2-宕机">9.3. 模拟从节点 node2 宕机</h3><p>继续使用命令 systemctl stop etcd 停掉节点 node2，查看集群状况，集群已经无法正常使用，说明 3 节点的 etcd 集群容错为 1。根据官方说明，集群可用性为 (N-1)/2，假设集群数量 N 是 3 台设备，可最多可故障 1 台设备，而不影响集群使用。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[root@node2 ~]# systemctl stop etcd.service</span><br><span class="line">[root@node2 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; endpoint status --write-out=table</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-01-06T12:39:58.307+0800&quot;,&quot;logger&quot;:&quot;etcd-client&quot;,&quot;caller&quot;:&quot;v3@v3.5.6/retry_interceptor.go:62&quot;,&quot;msg&quot;:&quot;retrying of unary invoker failed&quot;,&quot;target&quot;:&quot;etcd-endpoints://0xc00035aa80/node1.local:2379&quot;,&quot;attempt&quot;:0,&quot;error&quot;:&quot;rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error: desc = \&quot;transport: Error while dialing dial tcp 10.128.170.131:2379: connect: connection refused\&quot;&quot;&#125;</span><br><span class="line">Failed to get the status of endpoint https://node1.local:2379 (context deadline exceeded)</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-01-06T12:40:03.308+0800&quot;,&quot;logger&quot;:&quot;etcd-client&quot;,&quot;caller&quot;:&quot;v3@v3.5.6/retry_interceptor.go:62&quot;,&quot;msg&quot;:&quot;retrying of unary invoker failed&quot;,&quot;target&quot;:&quot;etcd-endpoints://0xc00035aa80/node1.local:2379&quot;,&quot;attempt&quot;:0,&quot;error&quot;:&quot;rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error: desc = \&quot;transport: Error while dialing dial tcp 10.128.170.132:2379: connect: connection refused\&quot;&quot;&#125;</span><br><span class="line">Failed to get the status of endpoint https://node2.local:2379 (context deadline exceeded)</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+-----------------------+</span><br><span class="line">|         ENDPOINT         |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX |        ERRORS         |</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+-----------------------+</span><br><span class="line">| https://node3.local:2379 | 57107ffa28aa353e |   3.5.6 |   20 kB |     false |      false |         4 |         16 |                 16 | etcdserver: no leader |</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+-----------------------+</span><br></pre></td></tr></table></figure><h3 id="模拟-node2-节点恢复">9.4. 模拟 node2 节点恢复</h3><p>使用命令 systemctl start etcd.service 启动节点 node2，查看集群状况，可见重新选举出 leader 节点，集群恢复正常使用：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">[root@node2 ~]# systemctl start etcd.service</span><br><span class="line">[root@node2 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; endpoint status --write-out=table</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-01-06T12:41:13.714+0800&quot;,&quot;logger&quot;:&quot;etcd-client&quot;,&quot;caller&quot;:&quot;v3@v3.5.6/retry_interceptor.go:62&quot;,&quot;msg&quot;:&quot;retrying of unary invoker failed&quot;,&quot;target&quot;:&quot;etcd-endpoints://0xc000332c40/node1.local:2379&quot;,&quot;attempt&quot;:0,&quot;error&quot;:&quot;rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error: desc = \&quot;transport: Error while dialing dial tcp 10.128.170.131:2379: connect: connection refused\&quot;&quot;&#125;</span><br><span class="line">Failed to get the status of endpoint https://node1.local:2379 (context deadline exceeded)</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br><span class="line">|         ENDPOINT         |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br><span class="line">| https://node2.local:2379 | 60084d96d6d6e98a |   3.5.6 |   20 kB |     false |      false |         5 |         18 |                 18 |        |</span><br><span class="line">| https://node3.local:2379 | 57107ffa28aa353e |   3.5.6 |   20 kB |      true |      false |         5 |         18 |                 18 |        |</span><br><span class="line">+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</span><br></pre></td></tr></table></figure><h2 id="etcd-故障节点修复">10. ETCD 故障节点修复</h2><h3 id="从集群中删除故障节点">10.1. 从集群中删除故障节点</h3><p><strong>（正常节点上操作）</strong></p><p>查看集群，获取坏掉节点的 ID，比如为 2cc5cd20ac23bb53</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; member list</span><br><span class="line">2cc5cd20ac23bb53, started, etcd01, https://10.128.170.131:2380, https://10.128.170.131:2379, false</span><br><span class="line">57107ffa28aa353e, started, etcd03, https://10.128.170.133:2380, https://10.128.170.133:2379, false</span><br><span class="line">60084d96d6d6e98a, started, etcd02, https://10.128.170.132:2380, https://10.128.170.132:2379, false</span><br></pre></td></tr></table></figure><p>删除坏掉的节点</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; member remove 2cc5cd20ac23bb53</span><br><span class="line">Member 2cc5cd20ac23bb53 removed from cluster 6fe4c34617a995fa</span><br></pre></td></tr></table></figure><h3 id="修复故障节点">10.2. 修复故障节点</h3><p><strong>（故障节点上操作）</strong></p><p>修改配置文件</p><p>把 ETCD_INITIAL_CLUSTER_STATE="new" 修改为 ETCD_INITIAL_CLUSTER_STATE="existing"</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# sed -i &#x27;s/ETCD_INITIAL_CLUSTER_STATE=&quot;new&quot;/ETCD_INITIAL_CLUSTER_STATE=&quot;existing&quot;/&#x27; /data/etcd/cfg/etcd.conf</span><br></pre></td></tr></table></figure><p>清理节点数据</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# rm -rf /data/etcd/default/member/</span><br></pre></td></tr></table></figure><h3 id="重新添加节点">10.3. 重新添加节点</h3><p><strong>（正常节点上操作）</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; member add etcd01 --peer-urls=https://10.128.170.131:2380</span><br><span class="line">Member cda884b84bce7043 added to cluster 6fe4c34617a995fa</span><br><span class="line"></span><br><span class="line">ETCD_NAME=&quot;etcd01&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER=&quot;etcd03=https://10.128.170.133:2380,etcd02=https://10.128.170.132:2380,etcd01=https://10.128.170.131:2380&quot;</span><br><span class="line">ETCD_INITIAL_ADVERTISE_PEER_URLS=&quot;https://10.128.170.131:2380&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER_STATE=&quot;existing&quot;</span><br></pre></td></tr></table></figure><h3 id="重启故障节点">10.4. 重启故障节点</h3><p><strong>（故障节点上操作）</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# systemctl start etcd.service</span><br></pre></td></tr></table></figure><h2 id="身份验证">11. 身份验证</h2><h3 id="创建-root-用户">11.1. 创建 root 用户</h3><p>先创建 root 用户，提示输入密码，这里我密码设置为 cluster</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; user add root</span><br><span class="line">Password of root:</span><br><span class="line">Type password of root again for confirmation:</span><br><span class="line">User root created</span><br></pre></td></tr></table></figure><p>赋予 root 用户 root 角色</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; user grant-role root root</span><br><span class="line">Role root is granted to user root</span><br></pre></td></tr></table></figure><h3 id="查看-root-用户详情">11.2. 查看 root 用户详情</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; user get root</span><br><span class="line">User: root</span><br><span class="line">Roles: root</span><br></pre></td></tr></table></figure><p><strong>确定 root 用户具有 root 角色拥有所有权限后再进行下一步</strong></p><h3 id="开启身份验证">11.3. 开启身份验证</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; auth enable</span><br><span class="line">Authentication Enabled</span><br></pre></td></tr></table></figure><h3 id="创建新用户">11.4. 创建新用户</h3><p>创建新用户 server，密码同样设为 cluster（可任意设置）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster user add server</span><br><span class="line">Password of server:</span><br><span class="line">Type password of server again for confirmation:</span><br><span class="line">User server created</span><br></pre></td></tr></table></figure><p>查看用户列表和 server 用户详情</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster user list</span><br><span class="line">root</span><br><span class="line">server</span><br><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster user get server</span><br><span class="line">User: server</span><br><span class="line">Roles:</span><br></pre></td></tr></table></figure><h3 id="创建角色">11.5. 创建角色</h3><p><strong>（内置的 root 角色无法通过 role list 查看，我们可以创建名字同样为 root 的角色，但是后续为 user 赋予 root 角色时，将使用自定义的 root 角色，而不是内置的 root 角色）</strong></p><p>创建 server 角色</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster role add server</span><br><span class="line">Role server created</span><br></pre></td></tr></table></figure><p>查看角色列表和 server 角色详情（新创建的角色没有任何权限）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster role list</span><br><span class="line">server</span><br><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster role get server</span><br><span class="line">Role server</span><br><span class="line">KV Read:</span><br><span class="line">KV Write:</span><br></pre></td></tr></table></figure><h3 id="角色授权">11.6. 角色授权</h3><p>角色没有密码，仅仅是定义的一组访问权限，角色的访问权限可以被赋予 read（读），write（写），readwrite（读和写）权限。</p><p>给 server 角色赋予键 /hello 读写操作权限</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster role grant-permission server readwrite /hello --prefix=true</span><br><span class="line">Role server updated</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster role get server</span><br><span class="line">Role server</span><br><span class="line">KV Read:</span><br><span class="line">        [/hello, /hellp) (prefix /hello)</span><br><span class="line">KV Write:</span><br><span class="line">        [/hello, /hellp) (prefix /hello)</span><br></pre></td></tr></table></figure><p>给 server 角色赋予键 /hello 目录读写操作权限</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster role grant-permission server readwrite /hello/* --prefix=true</span><br><span class="line">Role server updated</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster role get server</span><br><span class="line">Role server</span><br><span class="line">KV Read:</span><br><span class="line">        [/hello, /hellp) (prefix /hello)</span><br><span class="line">        [/hello/*, /hello/+) (prefix /hello/*)</span><br><span class="line">KV Write:</span><br><span class="line">        [/hello, /hellp) (prefix /hello)</span><br><span class="line">        [/hello/*, /hello/+) (prefix /hello/*)</span><br></pre></td></tr></table></figure><h3 id="赋予用户角色">11.7. 赋予用户角色</h3><p>赋予 server 用户 server 角色</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster user grant-role server server</span><br><span class="line">Role server is granted to user server</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster user get server</span><br><span class="line">User: server</span><br><span class="line">Roles: server</span><br></pre></td></tr></table></figure><h3 id="测试-server-用户权限">11.8. 测试 server 用户权限</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster put /hello world</span><br><span class="line">OK</span><br><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=root:cluster put /name wylu</span><br><span class="line">OK</span><br><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=server:cluster get /hello</span><br><span class="line">/hello</span><br><span class="line">world</span><br><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=server:cluster get /wylu</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-01-06T14:31:02.431+0800&quot;,&quot;logger&quot;:&quot;etcd-client&quot;,&quot;caller&quot;:&quot;v3@v3.5.6/retry_interceptor.go:62&quot;,&quot;msg&quot;:&quot;retrying of unary invoker failed&quot;,&quot;target&quot;:&quot;etcd-endpoints://0xc000558540/node1.local:2379&quot;,&quot;attempt&quot;:0,&quot;error&quot;:&quot;rpc error: code = PermissionDenied desc = etcdserver: permission denied&quot;&#125;</span><br><span class="line">Error: etcdserver: permission denied</span><br></pre></td></tr></table></figure><h4 id="不添加用户密码参数">11.8.1. 不添加用户密码参数</h4><p>提示错误：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; get /hello</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-01-06T14:32:37.153+0800&quot;,&quot;logger&quot;:&quot;etcd-client&quot;,&quot;caller&quot;:&quot;v3@v3.5.6/retry_interceptor.go:62&quot;,&quot;msg&quot;:&quot;retrying of unary invoker failed&quot;,&quot;target&quot;:&quot;etcd-endpoints://0xc00034a380/node1.local:2379&quot;,&quot;attempt&quot;:0,&quot;error&quot;:&quot;rpc error: code = InvalidArgument desc = etcdserver: user name is empty&quot;&#125;</span><br><span class="line">Error: etcdserver: user name is empty</span><br></pre></td></tr></table></figure><h4 id="添加用户密码参数">11.8.2. 添加用户密码参数</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@node3 ~]# /opt/etcd-v3.5.6-linux-amd64/bin/etcdctl --cacert=/data/etcd/ssl/ca.pem --cert=/data/etcd/ssl/server.pem --key=/data/etcd/ssl/server-key.pem --endpoints=&quot;https://node1.local:2379,https://node2.local:2379,https://node3.local:2379&quot; --user=server:cluster get /hello</span><br><span class="line">/hello</span><br><span class="line">world</span><br></pre></td></tr></table></figure><h3 id="常用命令附录">11.9. 常用命令附录</h3><h4 id="root-用户与-root-角色">11.9.1. root 用户与 root 角色</h4><p>root 是 etcd 的超级管理员，拥有 etcd 的所有权限，在开启角色认证之前为们必须要先建立好 root 用户。还需要注意的是 root 用户必须拥有 root 的角色，允许在 etcd 的所有操作。</p><p>root 角色可以赋予任何用户，拥有 root 角色的用户有全局读写权限和集群身份验证配置权限，此外，还具有修改集群成员身份，碎片整理，建立快照等权限。</p><h4 id="用户操作">11.9.2. 用户操作</h4><p>user：可以为 etcd 创建多个用户并设置密码，子命令有：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">1）add 添加用户</span><br><span class="line">2）delete 删除用户</span><br><span class="line">3）get 取得用户详情</span><br><span class="line">4）list 列出所有用户</span><br><span class="line">5）passwd 修改用户密码</span><br><span class="line">6）grant-role 给用户分配角色</span><br><span class="line">7）revoke-role 给用户移除角色</span><br></pre></td></tr></table></figure><h4 id="角色操作">11.9.3. 角色操作</h4><p>role：可以为 etcd 创建多个角色并设置权限，子命令有：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">1）add 添加角色</span><br><span class="line">2）delete 删除角色</span><br><span class="line">3）get 取得角色信息</span><br><span class="line">4）list 列出所有角色</span><br><span class="line">5）grant-permission 为角色设置某个key的权限</span><br><span class="line">6）revoke-permission 为角色移除某个key的权限</span><br></pre></td></tr></table></figure><h4 id="身份验证-1">11.9.4. 身份验证</h4><p>启动：auth enable</p><p>关闭：auth disable</p><h4 id="命令参考链接">11.9.5. 命令参考链接</h4><p><a href="https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/authentication.md" class="uri">https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/authentication.md</a></p><p><a href="https://juejin.im/post/5b986abff265da0ad947b52f" class="uri">https://juejin.im/post/5b986abff265da0ad947b52f</a></p><h2 id="安装-etcdkeeper">12. 安装 etcdkeeper</h2><p>etcdkeeper 可提供 web 页面来管理 etcd 数据库中的数据</p><h3 id="安装-etcdkeeper-1">12.1. 安装 etcdkeeper</h3><p>到 <a href="https://github.com/evildecay/etcdkeeper/releases/tag/v0.7.6" class="uri">https://github.com/evildecay/etcdkeeper/releases/tag/v0.7.6</a> 下载以下文件：</p><ul><li>etcdkeeper-v0.7.6-linux_x86_64.zip</li></ul><p>解压安装：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">unzip etcdkeeper-v0.7.6-linux_x86_64.zip -d /opt</span><br><span class="line">chmod +x /opt/etcdkeeper/etcdkeeper</span><br></pre></td></tr></table></figure><h3 id="创建-system-服务">12.2. 创建 system 服务</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">cat &gt; /usr/lib/systemd/system/etcdkeeper.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=etcdkeeper service</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=simple</span><br><span class="line">ExecStart=/opt/etcdkeeper/etcdkeeper \</span><br><span class="line">-h 0.0.0.0 \</span><br><span class="line">-p 8800 \</span><br><span class="line">-cacert=/data/etcd/ssl/ca.pem \</span><br><span class="line">-cert=/data/etcd/ssl/server.pem \</span><br><span class="line">-key=/data/etcd/ssl/server-key.pem \</span><br><span class="line">-auth \</span><br><span class="line">-usetls</span><br><span class="line">ExecReload=/bin/kill -HUP $MAINPID</span><br><span class="line">KillMode=process</span><br><span class="line">Restart=on-failure</span><br><span class="line">PrivateTmp=true</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><h3 id="修改-index.html">12.3. 修改 index.html</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@node1 Downloads]# dos2unix /opt/etcdkeeper/assets/etcdkeeper/index.html</span><br><span class="line">dos2unix: converting file /opt/etcdkeeper/assets/etcdkeeper/index.html to Unix format ...</span><br><span class="line">[root@node1 Downloads]# sed -i &quot;154s/etcdBase = &#x27;127.0.0.1:2379&#x27;/etcdBase = &#x27;10.128.170.131:2379&#x27;/&quot; /opt/etcdkeeper/assets/etcdkeeper/index.html</span><br></pre></td></tr></table></figure><h3 id="服务控制">12.4. 服务控制</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl enable etcdkeeper.service # 设置开机自启动</span><br><span class="line">systemctl disable etcdkeeper.service # 停止开机自启动</span><br><span class="line">systemctl start etcdkeeper # 启动 etcdkeeper 服务</span><br><span class="line">systemctl stop etcdkeeper # 停止 etcdkeeper 服务</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了在 Rocky Linux 系统上部署 etcd 集群的步骤。首先介绍了环境准备，包括三个节点的操作系统、IP地址和版本等信息。接下来，安装必要软件，包括 etcd、证书工具 cfssl 等，并创建 etcd 所需的证书。然后配置 etcd，包括主节点和启动配置，以及其余节点的配置。启动 etcd 服务并验证其状态，包括检查集群健康状态、查看集群节点列表、验证集群数据存储服务和监测数据变化等。此外，还介绍了数据备份与恢复、节点宕机与恢复、ETCD 故障节点修复和身份验证等内容。最后，介绍了如何安装 etcdkeeper。&lt;/p&gt;</summary>
    
    
    
    <category term="middleware" scheme="https://www.wylu.me/categories/middleware/"/>
    
    <category term="distributed coordination" scheme="https://www.wylu.me/categories/middleware/distributed-coordination/"/>
    
    <category term="etcd" scheme="https://www.wylu.me/categories/middleware/distributed-coordination/etcd/"/>
    
    
    <category term="etcd" scheme="https://www.wylu.me/tags/etcd/"/>
    
    <category term="RockyLinux" scheme="https://www.wylu.me/tags/RockyLinux/"/>
    
  </entry>
  
  <entry>
    <title>Windows11 WSL2 深度学习环境配置</title>
    <link href="https://www.wylu.me/posts/f88ecff5/"/>
    <id>https://www.wylu.me/posts/f88ecff5/</id>
    <published>2023-08-02T15:27:17.000Z</published>
    <updated>2023-08-02T15:29:19.486Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了在 Windows 11 上使用 WSL2 配置深度学习环境的方法。首先需要安装 NVIDIA 显卡驱动程序以获得 GPU 支持，推荐使用官方工具 GeForce Experience 进行安装或更新。然后安装 WSL2，并在其中安装 CUDA 和 cuDNN。在测试中，使用 CPU 运算和 GPU 运算分别运行了小规模和大规模神经网络，并打印了运行时间。结果表明，使用 GPU 运算可以显著提高运行速度。</p><span id="more"></span><h1 id="windows11-wsl2-深度学习环境配置">Windows11 WSL2 深度学习环境配置</h1><p><a href="https://docs.nvidia.com/cuda/wsl-user-guide/index.html">CUDA on WSL User Guide</a></p><h2 id="安装-nvidia-驱动程序以获得-gpu-支持">1. 安装 NVIDIA 驱动程序以获得 GPU 支持</h2><h3 id="方法一推荐">1.1 方法一（推荐）</h3><p>英伟达显卡驱动安装或者更新最好的方法就是利用官方的工具 <a href="https://www.nvidia.cn/geforce/geforce-experience/">GeForce Experience</a> 来进行，每一次显卡驱动更新后 CUDA 支持的最高版本会发生变化。</p><p>从 <a href="https://www.nvidia.cn/geforce/geforce-experience/" class="uri">https://www.nvidia.cn/geforce/geforce-experience/</a> 上下载 GeForce Experience 安装后，进入程序安装/更新 GeForce Game Ready 驱动程序。</p><h3 id="方法二">1.2 方法二</h3><p>从 <a href="https://www.nvidia.com/Download/index.aspx?lang=en-us" class="uri">https://www.nvidia.com/Download/index.aspx?lang=en-us</a> 获取机器对应显卡版本的驱动程序，然后在系统上安装 NVIDIA GeForce Game Ready 或 NVIDIA RTX Quadro Windows 11 显示驱动程序。</p><p><strong>注意：这是您需要安装的唯一驱动程序。 不要在 WSL 中安装任何 Linux 显示驱动程序。</strong></p><h2 id="安装-wsl2">2. 安装 WSL2</h2><p>启动您首选的 Windows Terminal / Command Prompt / Powershell 并安装 WSL：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wsl.exe <span class="literal">--install</span></span><br></pre></td></tr></table></figure><p>确保您拥有最新的 WSL 内核：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wsl.exe <span class="literal">--update</span></span><br></pre></td></tr></table></figure><p>查看 WSL 内核版本：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">PS</span> Microsoft.PowerShell.Core\FileSystem::\\wsl<span class="variable">$</span>\Ubuntu\root&gt; wsl <span class="built_in">cat</span> /proc/version</span><br><span class="line">Linux version <span class="number">5.10</span>.<span class="number">102.1</span><span class="literal">-microsoft-standard-WSL2</span> (oe<span class="literal">-user</span>@oe<span class="literal">-host</span>) (x86_64<span class="literal">-msft-linux-gcc</span> (GCC) <span class="number">9.3</span>.<span class="number">0</span>, GNU ld (GNU Binutils) <span class="number">2.34</span>.<span class="number">0.20200220</span>) <span class="comment">#1 SMP Wed Mar 2 00:30:59 UTC 2022</span></span><br></pre></td></tr></table></figure><p><strong>注意：<a href="https://docs.nvidia.com/cuda/wsl-user-guide/index.html#wsl2-system-requirements">WSL 2 Support Constraints</a></strong></p><p><strong>Ensure you are on the latest WSL Kernel or at least 4.19.121+. We recommend 5.10.16.3 or later for better performance and functional fixes.</strong></p><h2 id="安装-cuda">3. 安装 CUDA</h2><h3 id="确认-nvidia-驱动支持的-cuda-版本">3.1 确认 NVIDIA 驱动支持的 CUDA 版本</h3><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">C:\Windows&gt;nvidia<span class="literal">-smi</span></span><br><span class="line">Wed Jul <span class="number">27</span> <span class="number">22</span>:<span class="number">57</span>:<span class="number">07</span> <span class="number">2022</span></span><br><span class="line">+<span class="literal">-----------------------------------------------------------------------------</span>+</span><br><span class="line">| NVIDIA<span class="literal">-SMI</span> <span class="number">516.59</span>       Driver Version: <span class="number">516.59</span>       CUDA Version: <span class="number">11.7</span>     |</span><br><span class="line">|<span class="literal">-------------------------------</span>+<span class="literal">----------------------</span>+<span class="literal">----------------------</span>+</span><br><span class="line">| GPU  Name            TCC/WDDM | Bus<span class="literal">-Id</span>        Disp.A | Volatile Uncorr. ECC |</span><br><span class="line">| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory<span class="literal">-Usage</span> | GPU<span class="literal">-Util</span>  Compute M. |</span><br><span class="line">|                               |                      |               MIG M. |</span><br><span class="line">|===============================+======================+======================|</span><br><span class="line">|   <span class="number">0</span>  NVIDIA GeForce ... WDDM  | <span class="number">00000000</span>:<span class="number">01</span>:<span class="number">00.0</span> Off |                  N/A |</span><br><span class="line">| N/A   <span class="number">42</span>C    P0    <span class="number">11</span>W /  N/A |      <span class="number">0</span>MiB /  <span class="number">4096</span>MiB |      <span class="number">0</span>%      Default |</span><br><span class="line">|                               |                      |                  N/A |</span><br><span class="line">+<span class="literal">-------------------------------</span>+<span class="literal">----------------------</span>+<span class="literal">----------------------</span>+</span><br><span class="line"></span><br><span class="line">+<span class="literal">-----------------------------------------------------------------------------</span>+</span><br><span class="line">| Processes:                                                                  |</span><br><span class="line">|  GPU   <span class="built_in">GI</span>   CI        PID   <span class="built_in">Type</span>   <span class="keyword">Process</span> name                  GPU Memory |</span><br><span class="line">|        ID   ID                                                   Usage      |</span><br><span class="line">|=============================================================================|</span><br><span class="line">|  No running processes found                                                 |</span><br><span class="line">+<span class="literal">-----------------------------------------------------------------------------</span>+</span><br><span class="line"></span><br><span class="line">C:\Windows&gt;nvidia<span class="literal">-smi</span> <span class="literal">-L</span></span><br><span class="line">GPU <span class="number">0</span>: NVIDIA GeForce RTX <span class="number">3050</span> Laptop GPU (UUID: GPU<span class="literal">-f55a7fb2-0dc9-1d4f-dca9-8f7ea311ec67</span>)</span><br></pre></td></tr></table></figure><ul><li>NVIDIA 显卡硬件版本为：NVIDIA GeForce RTX 3050 Laptop GPU</li><li>当前显卡驱动程序版本为 ：516.59</li><li>CUDA 版本为：11.7</li></ul><p>意味着我笔记本当前的显卡驱动版本 516.59 最高支持的 CUDA 版本是 11.7（11.7 以下都是适配/兼容的）。可以查询 NVIDIA 显卡硬件版本 NVIDIA GeForce RTX 3050 Laptop 所支持的最高显卡驱动版本，通过升级显卡驱动后，来支持更高的 CUDA 版本。</p><p>详细的显卡驱动和 CUDA 的版本关系请参考</p><ul><li><a href="https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html">Table 2. CUDA Toolkit and Minimum Required Driver Version for CUDA Minor Version Compatibility</a></li><li><a href="https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html">Table 3. CUDA Toolkit and Corresponding Driver Versions</a></li></ul><h3 id="下载-cuda">3.2 下载 CUDA</h3><p>从 <a href="https://developer.nvidia.com/cuda-toolkit-archive" class="uri">https://developer.nvidia.com/cuda-toolkit-archive</a> 获取合适版本的 CUDA Toolkit，这里以 11.2.2 版本为例进行说明。</p><ul><li>进入 <a href="https://developer.nvidia.com/cuda-11-2-2-download-archive">CUDA Toolkit 11.2.2</a> 下载页面</li><li>选择 Linux 操作系统</li><li>选择 x86_64 架构</li><li>选择 WSL-Ubuntu 发行版</li><li>选择 runfile(local) 安装类型</li></ul><p>即可得到安装指示信息：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">wget https://developer.download.nvidia.com/compute/cuda/11.2.2/local_installers/cuda_11.2.2_460.32.03_linux.run</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo sh cuda_11.2.2_460.32.03_linux.run</span></span><br></pre></td></tr></table></figure><h3 id="在-wsl2-中安装-cuda">3.3 在 WSL2 中安装 CUDA</h3><p>根据上一步获取的安装指示，下载安装程序：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget -c https://developer.download.nvidia.com/compute/cuda/11.2.2/local_installers/cuda_11.2.2_460.32.03_linux.run</span><br></pre></td></tr></table></figure><p>执行安装程序：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh cuda_11.2.2_460.32.03_linux.run</span><br></pre></td></tr></table></figure><p>输入 accept 回车确认同意用户使用协议，光标移至 Install，安装全部，安装完成后会打印如下提示：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">===========</span><br><span class="line">= Summary =</span><br><span class="line">===========</span><br><span class="line"></span><br><span class="line">Driver:   Not Selected</span><br><span class="line">Toolkit:  Installed in /usr/local/cuda-11.2/</span><br><span class="line">Samples:  Installed in /root/, but missing recommended libraries</span><br><span class="line"></span><br><span class="line">Please make sure that</span><br><span class="line"> -   PATH includes /usr/local/cuda-11.2/bin</span><br><span class="line"> -   LD_LIBRARY_PATH includes /usr/local/cuda-11.2/lib64, or, add /usr/local/cuda-11.2/lib64 to /etc/ld.so.conf and run ldconfig as root</span><br><span class="line"></span><br><span class="line">To uninstall the CUDA Toolkit, run cuda-uninstaller in /usr/local/cuda-11.2/bin</span><br><span class="line">***WARNING: Incomplete installation! This installation did not install the CUDA Driver. A driver of version at least 460.00 is required for CUDA 11.2 functionality to work.</span><br><span class="line">To install the driver using this installer, run the following command, replacing &lt;CudaInstaller&gt; with the name of this run file:</span><br><span class="line">    sudo &lt;CudaInstaller&gt;.run --silent --driver</span><br><span class="line"></span><br><span class="line">Logfile is /var/log/cuda-installer.log</span><br></pre></td></tr></table></figure><p>提示中的警告 "WARNING: Incomplete installation! This installation did not install the CUDA Driver." 是正常的，因为我们只需在 Windows 下安装了 NVIDIA 驱动，而非 WSL 下。</p><h3 id="设置环境变量">3.4 设置环境变量</h3><p>编辑 /etc/profile 或 ~/.bashrc 文件，以 ~/.bashrc 为例</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim ~/.bashrc</span><br></pre></td></tr></table></figure><p>在文件末尾添加如下配置：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># Cuda Environment</span><br><span class="line">export CUDA_HOME=/usr/local/cuda-11.2</span><br><span class="line">export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH</span><br><span class="line"></span><br><span class="line">export PATH=$CUDA_HOME/bin:$PATH</span><br></pre></td></tr></table></figure><p>使配置生效</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="测试-cuda">3.5 测试 CUDA</h3><p>进入你的 CUDA Example 所在目录，默认是主目录，找到 "NVIDIA_CUDA-11.2_Samples"。依次打开 "1_Utilities" –&gt; "deviceQuery"，然后重新打开一个终端输入：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line">root@ubuntu:~# cd ~/NVIDIA_CUDA-11.2_Samples/1_Utilities/deviceQuery</span><br><span class="line">root@ubuntu:~/NVIDIA_CUDA-11.2_Samples/1_Utilities/deviceQuery# make</span><br><span class="line">/usr/local/cuda/bin/nvcc -ccbin g++ -I../../common/inc  -m64    --threads 0 -gencode arch=compute_35,code=sm_35 -gencode arch=compute_37,code=sm_37 -gencode arch=compute_50,code=sm_50 -gencode arch=compute_52,code=sm_52 -gencode arch=compute_60,code=sm_60 -gencode arch=compute_61,code=sm_61 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_75,code=sm_75 -gencode arch=compute_80,code=sm_80 -gencode arch=compute_86,code=sm_86 -gencode arch=compute_86,code=compute_86 -o deviceQuery.o -c deviceQuery.cpp</span><br><span class="line">nvcc warning : The &#x27;compute_35&#x27;, &#x27;compute_37&#x27;, &#x27;compute_50&#x27;, &#x27;sm_35&#x27;, &#x27;sm_37&#x27; and &#x27;sm_50&#x27; architectures are deprecated, and may be removed in a future release (Use -Wno-deprecated-gpu-targets to suppress warning).</span><br><span class="line">/usr/local/cuda/bin/nvcc -ccbin g++   -m64      -gencode arch=compute_35,code=sm_35 -gencode arch=compute_37,code=sm_37 -gencode arch=compute_50,code=sm_50 -gencode arch=compute_52,code=sm_52 -gencode arch=compute_60,code=sm_60 -gencode arch=compute_61,code=sm_61 -gencode arch=compute_70,code=sm_70 -gencode arch=compute_75,code=sm_75 -gencode arch=compute_80,code=sm_80 -gencode arch=compute_86,code=sm_86 -gencode arch=compute_86,code=compute_86 -o deviceQuery deviceQuery.o</span><br><span class="line">nvcc warning : The &#x27;compute_35&#x27;, &#x27;compute_37&#x27;, &#x27;compute_50&#x27;, &#x27;sm_35&#x27;, &#x27;sm_37&#x27; and &#x27;sm_50&#x27; architectures are deprecated, and may be removed in a future release (Use -Wno-deprecated-gpu-targets to suppress warning).</span><br><span class="line">mkdir -p ../../bin/x86_64/linux/release</span><br><span class="line">cp deviceQuery ../../bin/x86_64/linux/release</span><br><span class="line">root@ubuntu:~/NVIDIA_CUDA-11.2_Samples/1_Utilities/deviceQuery# ./deviceQuery</span><br><span class="line">./deviceQuery Starting...</span><br><span class="line"></span><br><span class="line"> CUDA Device Query (Runtime API) version (CUDART static linking)</span><br><span class="line"></span><br><span class="line">Detected 1 CUDA Capable device(s)</span><br><span class="line"></span><br><span class="line">Device 0: &quot;NVIDIA GeForce RTX 3050 Laptop GPU&quot;</span><br><span class="line">  CUDA Driver Version / Runtime Version          11.7 / 11.2</span><br><span class="line">  CUDA Capability Major/Minor version number:    8.6</span><br><span class="line">  Total amount of global memory:                 4096 MBytes (4294508544 bytes)</span><br><span class="line">  (16) Multiprocessors, (128) CUDA Cores/MP:     2048 CUDA Cores</span><br><span class="line">  GPU Max Clock rate:                            1057 MHz (1.06 GHz)</span><br><span class="line">  Memory Clock rate:                             5501 Mhz</span><br><span class="line">  Memory Bus Width:                              128-bit</span><br><span class="line">  L2 Cache Size:                                 1572864 bytes</span><br><span class="line">  Maximum Texture Dimension Size (x,y,z)         1D=(131072), 2D=(131072, 65536), 3D=(16384, 16384, 16384)</span><br><span class="line">  Maximum Layered 1D Texture Size, (num) layers  1D=(32768), 2048 layers</span><br><span class="line">  Maximum Layered 2D Texture Size, (num) layers  2D=(32768, 32768), 2048 layers</span><br><span class="line">  Total amount of constant memory:               65536 bytes</span><br><span class="line">  Total amount of shared memory per block:       49152 bytes</span><br><span class="line">  Total shared memory per multiprocessor:        102400 bytes</span><br><span class="line">  Total number of registers available per block: 65536</span><br><span class="line">  Warp size:                                     32</span><br><span class="line">  Maximum number of threads per multiprocessor:  1536</span><br><span class="line">  Maximum number of threads per block:           1024</span><br><span class="line">  Max dimension size of a thread block (x,y,z): (1024, 1024, 64)</span><br><span class="line">  Max dimension size of a grid size    (x,y,z): (2147483647, 65535, 65535)</span><br><span class="line">  Maximum memory pitch:                          2147483647 bytes</span><br><span class="line">  Texture alignment:                             512 bytes</span><br><span class="line">  Concurrent copy and kernel execution:          Yes with 1 copy engine(s)</span><br><span class="line">  Run time limit on kernels:                     Yes</span><br><span class="line">  Integrated GPU sharing Host Memory:            No</span><br><span class="line">  Support host page-locked memory mapping:       Yes</span><br><span class="line">  Alignment requirement for Surfaces:            Yes</span><br><span class="line">  Device has ECC support:                        Disabled</span><br><span class="line">  Device supports Unified Addressing (UVA):      Yes</span><br><span class="line">  Device supports Managed Memory:                Yes</span><br><span class="line">  Device supports Compute Preemption:            Yes</span><br><span class="line">  Supports Cooperative Kernel Launch:            Yes</span><br><span class="line">  Supports MultiDevice Co-op Kernel Launch:      No</span><br><span class="line">  Device PCI Domain ID / Bus ID / location ID:   0 / 1 / 0</span><br><span class="line">  Compute Mode:</span><br><span class="line">     &lt; Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) &gt;</span><br><span class="line"></span><br><span class="line">deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 11.7, CUDA Runtime Version = 11.2, NumDevs = 1</span><br><span class="line">Result = PASS</span><br></pre></td></tr></table></figure><p>出现 "Result = PASS" 字样时，说明安装成功</p><h2 id="安装-cudnn">4. 安装 cuDNN</h2><h3 id="下载-cudnn">4.1 下载 cuDNN</h3><p>从 <a href="https://developer.nvidia.com/rdp/cudnn-archive" class="uri">https://developer.nvidia.com/rdp/cudnn-archive</a> 获取符合 CUDA 版本的 cuDNN。这里示例 CUDA 是 11.2，所以选择了 <a href="https://developer.nvidia.com/rdp/cudnn-archive#a-collapse811-111">Download cuDNN v8.1.1 (Feburary 26th, 2021), for CUDA 11.0,11.1 and 11.2</a>，选择下载 <a href="https://developer.nvidia.com/compute/machine-learning/cudnn/secure/8.1.1.33/11.2_20210301/cudnn-11.2-linux-x64-v8.1.1.33.tgz">cuDNN Library for Linux (x86_64)</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget -c https://developer.nvidia.com/compute/machine-learning/cudnn/secure/8.1.1.33/11.2_20210301/cudnn-11.2-linux-x64-v8.1.1.33.tgz</span><br></pre></td></tr></table></figure><p><strong>注意：这里需要注册 NVIDIA 账号。</strong></p><h3 id="在-wsl2-中安装-cudnn">4.2 在 WSL2 中安装 cuDNN</h3><p>安装过程实际上是把 cuDNN 的头文件复制到 CUDA 的头文件目录里面去，把 cuDNN 的库复制到 CUDA 的库目录里面去。</p><p>解压：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">root@ubuntu:~/Downloads# tar -zxvf cudnn-11.2-linux-x64-v8.1.1.33.tgz</span><br><span class="line">cuda/include/cudnn.h</span><br><span class="line">cuda/include/cudnn_adv_infer.h</span><br><span class="line">cuda/include/cudnn_adv_infer_v8.h</span><br><span class="line">cuda/include/cudnn_adv_train.h</span><br><span class="line">cuda/include/cudnn_adv_train_v8.h</span><br><span class="line">cuda/include/cudnn_backend.h</span><br><span class="line">cuda/include/cudnn_backend_v8.h</span><br><span class="line">cuda/include/cudnn_cnn_infer.h</span><br><span class="line">cuda/include/cudnn_cnn_infer_v8.h</span><br><span class="line">cuda/include/cudnn_cnn_train.h</span><br><span class="line">cuda/include/cudnn_cnn_train_v8.h</span><br><span class="line">cuda/include/cudnn_ops_infer.h</span><br><span class="line">cuda/include/cudnn_ops_infer_v8.h</span><br><span class="line">cuda/include/cudnn_ops_train.h</span><br><span class="line">cuda/include/cudnn_ops_train_v8.h</span><br><span class="line">cuda/include/cudnn_v8.h</span><br><span class="line">cuda/include/cudnn_version.h</span><br><span class="line">cuda/include/cudnn_version_v8.h</span><br><span class="line">cuda/NVIDIA_SLA_cuDNN_Support.txt</span><br><span class="line">cuda/lib64/libcudnn.so</span><br><span class="line">cuda/lib64/libcudnn.so.8</span><br><span class="line">cuda/lib64/libcudnn.so.8.1.1</span><br><span class="line">cuda/lib64/libcudnn_adv_infer.so</span><br><span class="line">cuda/lib64/libcudnn_adv_infer.so.8</span><br><span class="line">cuda/lib64/libcudnn_adv_infer.so.8.1.1</span><br><span class="line">cuda/lib64/libcudnn_adv_train.so</span><br><span class="line">cuda/lib64/libcudnn_adv_train.so.8</span><br><span class="line">cuda/lib64/libcudnn_adv_train.so.8.1.1</span><br><span class="line">cuda/lib64/libcudnn_cnn_infer.so</span><br><span class="line">cuda/lib64/libcudnn_cnn_infer.so.8</span><br><span class="line">cuda/lib64/libcudnn_cnn_infer.so.8.1.1</span><br><span class="line">cuda/lib64/libcudnn_cnn_train.so</span><br><span class="line">cuda/lib64/libcudnn_cnn_train.so.8</span><br><span class="line">cuda/lib64/libcudnn_cnn_train.so.8.1.1</span><br><span class="line">cuda/lib64/libcudnn_ops_infer.so</span><br><span class="line">cuda/lib64/libcudnn_ops_infer.so.8</span><br><span class="line">cuda/lib64/libcudnn_ops_infer.so.8.1.1</span><br><span class="line">cuda/lib64/libcudnn_ops_train.so</span><br><span class="line">cuda/lib64/libcudnn_ops_train.so.8</span><br><span class="line">cuda/lib64/libcudnn_ops_train.so.8.1.1</span><br><span class="line">cuda/lib64/libcudnn_static.a</span><br><span class="line">cuda/lib64/libcudnn_static_v8.a</span><br></pre></td></tr></table></figure><p>复制 cuDNN 头文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp cuda/include/* /usr/local/cuda-11.2/include/</span><br></pre></td></tr></table></figure><p>复制 cuDNN 库文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp cuda/lib64/* /usr/local/cuda-11.2/lib64/</span><br></pre></td></tr></table></figure><p>添加可执行权限</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">chmod +x /usr/local/cuda-11.2/include/cudnn.h</span><br><span class="line">chmod +x /usr/local/cuda-11.2/lib64/libcudnn*</span><br></pre></td></tr></table></figure><h3 id="测试-cudnn">4.3 测试 cuDNN</h3><p>打开一个新的终端：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">root@ubuntu:~# conda activate ailab</span><br><span class="line">(ailab) root@ubuntu:~# ipython</span><br><span class="line">Python 3.7.13 (default, Mar 29 2022, 02:18:16)</span><br><span class="line">Type &#x27;copyright&#x27;, &#x27;credits&#x27; or &#x27;license&#x27; for more information</span><br><span class="line">IPython 7.34.0 -- An enhanced Interactive Python. Type &#x27;?&#x27; for help.</span><br><span class="line"></span><br><span class="line">In [1]: import tensorflow as tf</span><br><span class="line"></span><br><span class="line">In [2]: tf.test.is_gpu_available()</span><br><span class="line">WARNING:tensorflow:From &lt;ipython-input-2-17bb7203622b&gt;:1: is_gpu_available (from tensorflow.python.framework.test_util) is deprecated and will be removed in a future version.</span><br><span class="line">Instructions for updating:</span><br><span class="line">Use `tf.config.list_physical_devices(&#x27;GPU&#x27;)` instead.</span><br><span class="line">2022-07-27 23:46:19.645726: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA</span><br><span class="line">To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.</span><br><span class="line">2022-07-27 23:46:21.108297: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:961] could not open file to read NUMA node: /sys/bus/pci/devices/0000:01:00.0/numa_node</span><br><span class="line">Your kernel may have been built without NUMA support.</span><br><span class="line">2022-07-27 23:46:21.136684: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:961] could not open file to read NUMA node: /sys/bus/pci/devices/0000:01:00.0/numa_node</span><br><span class="line">Your kernel may have been built without NUMA support.</span><br><span class="line">2022-07-27 23:46:21.137199: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:961] could not open file to read NUMA node: /sys/bus/pci/devices/0000:01:00.0/numa_node</span><br><span class="line">Your kernel may have been built without NUMA support.</span><br><span class="line">2022-07-27 23:46:22.147971: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:961] could not open file to read NUMA node: /sys/bus/pci/devices/0000:01:00.0/numa_node</span><br><span class="line">Your kernel may have been built without NUMA support.</span><br><span class="line">2022-07-27 23:46:22.148598: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:961] could not open file to read NUMA node: /sys/bus/pci/devices/0000:01:00.0/numa_node</span><br><span class="line">Your kernel may have been built without NUMA support.</span><br><span class="line">2022-07-27 23:46:22.148635: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1616] Could not identify NUMA node of platform GPU id 0, defaulting to 0.  Your kernel may not have been built with NUMA support.</span><br><span class="line">2022-07-27 23:46:22.149146: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:961] could not open file to read NUMA node: /sys/bus/pci/devices/0000:01:00.0/numa_node</span><br><span class="line">Your kernel may have been built without NUMA support.</span><br><span class="line">2022-07-27 23:46:22.149214: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1532] Created device /device:GPU:0 with 1626 MB memory:  -&gt; device: 0, name: NVIDIA GeForce RTX 3050 Laptop GPU, pci bus id: 0000:01:00.0, compute capability: 8.6</span><br><span class="line">Out[2]: True</span><br><span class="line"></span><br><span class="line">In [3]:</span><br></pre></td></tr></table></figure><p>输出中提示 "Your kernel may not have been built with NUMA support" ，这是无害告警，可以忽略。</p><p><a href="https://stackoverflow.com/questions/51733128/your-kernel-may-have-been-built-without-numa-support" class="uri">https://stackoverflow.com/questions/51733128/your-kernel-may-have-been-built-without-numa-support</a></p><p><a href="https://forums.developer.nvidia.com/t/numa-error-running-tensorflow-on-jetson-tx2/56119" class="uri">https://forums.developer.nvidia.com/t/numa-error-running-tensorflow-on-jetson-tx2/56119</a></p><p><a href="https://stackoverflow.com/questions/40426502/is-there-a-way-to-suppress-the-messages-tensorflow-prints" class="uri">https://stackoverflow.com/questions/40426502/is-there-a-way-to-suppress-the-messages-tensorflow-prints</a></p><h2 id="cpu-vs-gpu">5. CPU vs GPU</h2><table><thead><tr class="header"><th>硬件</th><th>型号</th></tr></thead><tbody><tr class="odd"><td>CPU</td><td>AMD Ryzen 7 5800H with Radeon Graphics</td></tr><tr class="even"><td>GPU</td><td>NVIDIA GeForce RTX 3050 Laptop GPU</td></tr></tbody></table><h3 id="小规模神经网络">5.1 小规模神经网络</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment"># TensorFlow and tf.keras</span></span><br><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="comment"># Helper libraries</span></span><br><span class="line"><span class="keyword">from</span> time <span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">mnist = tf.keras.datasets.mnist</span><br><span class="line">(x_train, y_train), (x_test, y_test) = mnist.load_data()</span><br><span class="line">x_train, x_test = x_train / <span class="number">255.0</span>, x_test / <span class="number">255.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 用 CPU 运算</span></span><br><span class="line">startTime1 = time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> tf.device(<span class="string">&#x27;/cpu:0&#x27;</span>):</span><br><span class="line">    model = tf.keras.models.Sequential([</span><br><span class="line">        tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>, <span class="number">28</span>)),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">128</span>, activation=<span class="string">&#x27;relu&#x27;</span>),</span><br><span class="line">        tf.keras.layers.Dropout(<span class="number">0.2</span>),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">10</span>, activation=<span class="string">&#x27;softmax&#x27;</span>),</span><br><span class="line">    ])</span><br><span class="line">    model.<span class="built_in">compile</span>(</span><br><span class="line">        optimizer=<span class="string">&#x27;adam&#x27;</span>,</span><br><span class="line">        loss=<span class="string">&#x27;sparse_categorical_crossentropy&#x27;</span>,</span><br><span class="line">        metrics=[<span class="string">&#x27;accuracy&#x27;</span>],</span><br><span class="line">    )</span><br><span class="line">    model.fit(x_train, y_train, epochs=<span class="number">10</span>)</span><br><span class="line">    model.evaluate(x_test, y_test)</span><br><span class="line">t1 = time() - startTime1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 用 GPU 运算</span></span><br><span class="line">startTime2 = time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> tf.device(<span class="string">&#x27;/gpu:0&#x27;</span>):</span><br><span class="line">    model = tf.keras.models.Sequential([</span><br><span class="line">        tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>, <span class="number">28</span>)),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">128</span>, activation=<span class="string">&#x27;relu&#x27;</span>),</span><br><span class="line">        tf.keras.layers.Dropout(<span class="number">0.2</span>),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">10</span>, activation=<span class="string">&#x27;softmax&#x27;</span>),</span><br><span class="line">    ])</span><br><span class="line">    model.<span class="built_in">compile</span>(</span><br><span class="line">        optimizer=<span class="string">&#x27;adam&#x27;</span>,</span><br><span class="line">        loss=<span class="string">&#x27;sparse_categorical_crossentropy&#x27;</span>,</span><br><span class="line">        metrics=[<span class="string">&#x27;accuracy&#x27;</span>],</span><br><span class="line">    )</span><br><span class="line">    model.fit(x_train, y_train, epochs=<span class="number">10</span>)</span><br><span class="line">    model.evaluate(x_test, y_test)</span><br><span class="line"></span><br><span class="line">t2 = time() - startTime2</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印运行时间</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;使用cpu花的时间：&#x27;</span>, t1)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;使用gpu花的时间：&#x27;</span>, t2)</span><br></pre></td></tr></table></figure><p>运行结果</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">使用cpu花的时间： 26.906126737594604</span><br><span class="line">使用gpu花的时间： 54.50972127914429</span><br></pre></td></tr></table></figure><h3 id="大规模神经网络">5.2 大规模神经网络</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment"># TensorFlow and tf.keras</span></span><br><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="comment"># Helper libraries</span></span><br><span class="line"><span class="keyword">from</span> time <span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">mnist = tf.keras.datasets.mnist</span><br><span class="line">(x_train, y_train), (x_test, y_test) = mnist.load_data()</span><br><span class="line">x_train, x_test = x_train / <span class="number">255.0</span>, x_test / <span class="number">255.0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># CPU 运行</span></span><br><span class="line">startTime1 = time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> tf.device(<span class="string">&#x27;/cpu:0&#x27;</span>):</span><br><span class="line">    model = tf.keras.models.Sequential([</span><br><span class="line">        tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>, <span class="number">28</span>)),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">1000</span>, activation=<span class="string">&#x27;relu&#x27;</span>),</span><br><span class="line">        tf.keras.layers.Dropout(<span class="number">0.2</span>),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">1000</span>, activation=<span class="string">&#x27;relu&#x27;</span>),</span><br><span class="line">        tf.keras.layers.Dropout(<span class="number">0.2</span>),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">10</span>, activation=<span class="string">&#x27;softmax&#x27;</span>),</span><br><span class="line">    ])</span><br><span class="line">    model.<span class="built_in">compile</span>(</span><br><span class="line">        optimizer=<span class="string">&#x27;adam&#x27;</span>,</span><br><span class="line">        loss=<span class="string">&#x27;sparse_categorical_crossentropy&#x27;</span>,</span><br><span class="line">        metrics=[<span class="string">&#x27;accuracy&#x27;</span>],</span><br><span class="line">    )</span><br><span class="line">    model.fit(x_train, y_train, epochs=<span class="number">10</span>)</span><br><span class="line">    model.evaluate(x_test, y_test)</span><br><span class="line"></span><br><span class="line">t1 = time() - startTime1</span><br><span class="line"></span><br><span class="line"><span class="comment"># GPU 运行</span></span><br><span class="line">startTime2 = time()</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> tf.device(<span class="string">&#x27;/gpu:0&#x27;</span>):</span><br><span class="line">    model = tf.keras.models.Sequential([</span><br><span class="line">        tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>, <span class="number">28</span>)),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">1000</span>, activation=<span class="string">&#x27;relu&#x27;</span>),</span><br><span class="line">        tf.keras.layers.Dropout(<span class="number">0.2</span>),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">1000</span>, activation=<span class="string">&#x27;relu&#x27;</span>),</span><br><span class="line">        tf.keras.layers.Dropout(<span class="number">0.2</span>),</span><br><span class="line">        tf.keras.layers.Dense(<span class="number">10</span>, activation=<span class="string">&#x27;softmax&#x27;</span>),</span><br><span class="line">    ])</span><br><span class="line"></span><br><span class="line">    model.<span class="built_in">compile</span>(</span><br><span class="line">        optimizer=<span class="string">&#x27;adam&#x27;</span>,</span><br><span class="line">        loss=<span class="string">&#x27;sparse_categorical_crossentropy&#x27;</span>,</span><br><span class="line">        metrics=[<span class="string">&#x27;accuracy&#x27;</span>],</span><br><span class="line">    )</span><br><span class="line">    model.fit(x_train, y_train, epochs=<span class="number">10</span>)</span><br><span class="line">    model.evaluate(x_test, y_test)</span><br><span class="line"></span><br><span class="line">t2 = time() - startTime2</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印运行时间</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;使用cpu花的时间：&#x27;</span>, t1)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;使用gpu花的时间：&#x27;</span>, t2)</span><br></pre></td></tr></table></figure><p>运行结果</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">使用 cpu 花的时间：205.31678819656372</span><br><span class="line">使用 gpu 花的时间：74.53320002555847</span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://zhuanlan.zhihu.com/p/434239083">深度学习环境配置 Windows+WSL2</a></p><p><a href="https://zhuanlan.zhihu.com/p/344950101">Windows搭建深度学习环境(CUDA)</a></p><p><a href="https://blog.csdn.net/public669/article/details/98470857">ubuntu安装cudnn</a></p><p><a href="https://docs.nvidia.com/cuda/wsl-user-guide/index.html#abstract">CUDA on WSL User Guide</a></p><p><a href="https://blog.csdn.net/zqj6893/article/details/9040141">linux下查看库是否存在</a></p><p><a href="https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html#abstract">NVIDIA CUDA Toolkit Release Notes</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了在 Windows 11 上使用 WSL2 配置深度学习环境的方法。首先需要安装 NVIDIA 显卡驱动程序以获得 GPU 支持，推荐使用官方工具 GeForce Experience 进行安装或更新。然后安装 WSL2，并在其中安装 CUDA 和 cuDNN。在测试中，使用 CPU 运算和 GPU 运算分别运行了小规模和大规模神经网络，并打印了运行时间。结果表明，使用 GPU 运算可以显著提高运行速度。&lt;/p&gt;</summary>
    
    
    
    <category term="os" scheme="https://www.wylu.me/categories/os/"/>
    
    <category term="windows" scheme="https://www.wylu.me/categories/os/windows/"/>
    
    
    <category term="深度学习" scheme="https://www.wylu.me/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="WSL2" scheme="https://www.wylu.me/tags/WSL2/"/>
    
    <category term="CUDA" scheme="https://www.wylu.me/tags/CUDA/"/>
    
  </entry>
  
  <entry>
    <title>bitnami etcd 故障成员恢复</title>
    <link href="https://www.wylu.me/posts/4a464bac/"/>
    <id>https://www.wylu.me/posts/4a464bac/</id>
    <published>2023-08-01T15:52:32.000Z</published>
    <updated>2023-08-01T15:56:33.025Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了在使用 bitnami/etcd 部署的集群中，如何恢复故障成员的方法。首先在处理前，往 etcd 集群中添加一些数据，以验证恢复方案不会导致集群数据丢失。然后，通过删除 etcd-1 pv 的数据目录来模拟 etcd-1 成员故障，并查看 etcd-1 pod 日志、etcd 集群成员状态以及 endpoint 健康状态来分析故障原因。接着，本文提供了一套恢复方案，包括将故障成员 etcd-1 从集群中移除、将 etcd statefulsets 缩放为 0、删除故障成员 etcd-1 绑定的 pv 的数据目录、修改 statefulsets 中 ETCD_INITIAL_CLUSTER_STATE 配置的值为 "existing"、将 etcd statefulsets 恢复为原来的副本数以及检查 etcd 集群 endpoint 健康状态等步骤。最后，验证数据是否完整。</p><span id="more"></span><h1 id="bitnami-etcd-故障成员恢复">bitnami etcd 故障成员恢复</h1><h2 id="环境准备">环境准备</h2><p>准备一个使用 bitnami/etcd 部署的集群。</p><p>查看 statefulsets：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd get statefulsets.apps</span><br><span class="line">NAME          READY   AGE</span><br><span class="line">etcd          3/3     29d</span><br></pre></td></tr></table></figure><p>查看 pod：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd get pod</span><br><span class="line">NAME            READY   STATUS      RESTARTS       AGE</span><br><span class="line">etcd-0          1/1     Running     0              19m</span><br><span class="line">etcd-1          1/1     Running     0              21m</span><br><span class="line">etcd-2          1/1     Running     0              22m</span><br></pre></td></tr></table></figure><p>查看 <code>ETCD_INITIAL_CLUSTER_STATE</code> 配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd get statefulsets.apps etcd -o yaml | grep ETCD_INITIAL_CLUSTER_STATE -A 1</span><br><span class="line">        - name: ETCD_INITIAL_CLUSTER_STATE</span><br><span class="line">          value: new</span><br></pre></td></tr></table></figure><p>查看 etcd 集群成员状态：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl member list</span><br><span class="line">14105f2e4f2559a9, started, etcd-0, http://etcd-0.etcd-headless.etcd:2380, http://etcd-0.etcd-headless.etcd:2379,http://etcd.etcd:2379, false</span><br><span class="line">432042ce92be9a79, started, etcd-2, http://etcd-2.etcd-headless.etcd:2380, http://etcd-2.etcd-headless.etcd:2379,http://etcd.etcd:2379, false</span><br><span class="line">e3ff14c8562cf8c2, started, etcd-1, http://etcd-1.etcd-headless.etcd:2380, http://etcd-1.etcd-headless.etcd:2379,http://etcd.etcd:2379, false</span><br></pre></td></tr></table></figure><p>该集群有 3 个成员：</p><ul><li>etcd-0</li><li>etcd-1</li><li>etcd-2</li></ul><h2 id="添加测试数据">添加测试数据</h2><p>在处理前，先往 etcd 集群中添加一些数据，以验证恢复方案不会导致集群数据丢失。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd exec -it etcd-0 -- bash</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl put /hello world</span><br><span class="line">OK</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl get /hello</span><br><span class="line">/hello</span><br><span class="line">world</span><br></pre></td></tr></table></figure><h2 id="破坏-etcd-1-成员">破坏 etcd-1 成员</h2><p>获取 etcd-1 的 pv：<strong>（所有 pv 均使用 local-storage）</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd get pvc</span><br><span class="line">NAME                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS    AGE</span><br><span class="line">data-etcd-0          Bound    pvc-2a2f2a25-69b3-41ab-a21d-a5aa3d05277d   10Gi       RWO            local-storage   29d</span><br><span class="line">data-etcd-1          Bound    pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20   10Gi       RWO            local-storage   60m</span><br><span class="line">data-etcd-2          Bound    pvc-6da8bcd7-cd2a-43fc-9849-7e1c2870f193   10Gi       RWO            local-storage   29d</span><br></pre></td></tr></table></figure><p>获取 etcd-1 pv 所在的节点：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl get pv pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20 -o yaml | grep nodeAffinity -A 7</span><br><span class="line">  nodeAffinity:</span><br><span class="line">    required:</span><br><span class="line">      nodeSelectorTerms:</span><br><span class="line">      - matchExpressions:</span><br><span class="line">        - key: kubernetes.io/hostname</span><br><span class="line">          operator: In</span><br><span class="line">          values:</span><br><span class="line">          - cnode3</span><br></pre></td></tr></table></figure><p>获取 etcd-1 pv 的数据目录：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl get pv pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20 -o yaml | grep hostPath -A 2</span><br><span class="line">  hostPath:</span><br><span class="line">    path: /data/local-path-provisioner/pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20_etcd_data-etcd-1</span><br><span class="line">    type: DirectoryOrCreate</span><br></pre></td></tr></table></figure><p>进入节点 cnode3 后台：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh root@cnode3</span><br></pre></td></tr></table></figure><p>删除 etcd-1 pv 的数据目录：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode3 ~]# rm -rf /data/local-path-provisioner/pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20_etcd_data-etcd-1/data/</span><br></pre></td></tr></table></figure><p>删除 etcd-1 pod：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd delete pod etcd-1</span><br><span class="line">pod &quot;etcd-1&quot; deleted</span><br></pre></td></tr></table></figure><p>等待 etcd-1 pod 重新被拉起：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd get pod</span><br><span class="line">NAME            READY   STATUS             RESTARTS       AGE</span><br><span class="line">etcd-0          1/1     Running            0              46m</span><br><span class="line">etcd-1          0/1     CrashLoopBackOff   5 (114s ago)   5m10s</span><br><span class="line">etcd-2          1/1     Running            0              48m</span><br></pre></td></tr></table></figure><p>此状态下，重启 etcd 集群也无法恢复：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd rollout restart statefulset etcd</span><br><span class="line">statefulset.apps/etcd restarted</span><br><span class="line">[root@cnode1 ~]# kubectl -n etcd scale statefulset etcd --replicas=0</span><br><span class="line">statefulset.apps/etcd scaled</span><br><span class="line">[root@cnode1 ~]# kubectl -n etcd scale statefulset etcd --replicas=3</span><br><span class="line">statefulset.apps/etcd scaled</span><br><span class="line">[root@cnode1 ~]# kubectl -n etcd get pod</span><br><span class="line">NAME            READY   STATUS             RESTARTS       AGE</span><br><span class="line">etcd-0          1/1     Running            0              67s</span><br><span class="line">etcd-1          0/1     CrashLoopBackOff   1 (12s ago)    67s</span><br><span class="line">etcd-2          1/1     Running            0              67s</span><br></pre></td></tr></table></figure><h2 id="分析-etcd-1-故障">分析 etcd-1 故障</h2><p>查看 etcd-1 pod 日志：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd logs -f etcd-1</span><br><span class="line">...</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.938+0800&quot;,&quot;caller&quot;:&quot;flags/flag.go:113&quot;,&quot;msg&quot;:&quot;recognized and used environment variable&quot;,&quot;variable-name&quot;:&quot;ETCD_INITIAL_ADVERTISE_PEER_URLS&quot;,&quot;variable-value&quot;:&quot;http://etcd-1.etcd-headless.etcd:2380&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.938+0800&quot;,&quot;caller&quot;:&quot;flags/flag.go:113&quot;,&quot;msg&quot;:&quot;recognized and used environment variable&quot;,&quot;variable-name&quot;:&quot;ETCD_INITIAL_CLUSTER&quot;,&quot;variable-value&quot;:&quot;etcd-0=http://etcd-0.etcd-headless.etcd:2380,etcd-1=http://etcd-1.etcd-headless.etcd:2380,etcd-2=http://etcd-2.etcd-headless.etcd:2380&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.938+0800&quot;,&quot;caller&quot;:&quot;flags/flag.go:113&quot;,&quot;msg&quot;:&quot;recognized and used environment variable&quot;,&quot;variable-name&quot;:&quot;ETCD_INITIAL_CLUSTER_STATE&quot;,&quot;variable-value&quot;:&quot;new&quot;&#125;</span><br><span class="line">...</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.939+0800&quot;,&quot;caller&quot;:&quot;etcdmain/etcd.go:73&quot;,&quot;msg&quot;:&quot;Running: &quot;,&quot;args&quot;:[&quot;etcd&quot;]&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.939+0800&quot;,&quot;caller&quot;:&quot;etcdmain/etcd.go:446&quot;,&quot;msg&quot;:&quot;found invalid file under data directory&quot;,&quot;filename&quot;:&quot;member_id&quot;,&quot;data-dir&quot;:&quot;/bitnami/etcd/data&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.939+0800&quot;,&quot;caller&quot;:&quot;etcdmain/etcd.go:116&quot;,&quot;msg&quot;:&quot;server has been already initialized&quot;,&quot;data-dir&quot;:&quot;/bitnami/etcd/data&quot;,&quot;dir-type&quot;:&quot;member&quot;&#125;</span><br><span class="line">...</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.957+0800&quot;,&quot;logger&quot;:&quot;raft&quot;,&quot;caller&quot;:&quot;etcdserver/zap_raft.go:77&quot;,&quot;msg&quot;:&quot;af3fef14dc164ad9 [term: 1] received a MsgHeartbeat message with higher term from 14105f2e4f2559a9 [term: 24]&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.957+0800&quot;,&quot;logger&quot;:&quot;raft&quot;,&quot;caller&quot;:&quot;etcdserver/zap_raft.go:77&quot;,&quot;msg&quot;:&quot;af3fef14dc164ad9 became follower at term 24&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.957+0800&quot;,&quot;logger&quot;:&quot;raft&quot;,&quot;caller&quot;:&quot;etcdserver/zap_raft.go:77&quot;,&quot;msg&quot;:&quot;raft.node: af3fef14dc164ad9 elected leader 14105f2e4f2559a9 at term 24&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.958+0800&quot;,&quot;caller&quot;:&quot;etcdserver/server.go:1150&quot;,&quot;msg&quot;:&quot;server error&quot;,&quot;error&quot;:&quot;the member has been permanently removed from the cluster&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.958+0800&quot;,&quot;caller&quot;:&quot;etcdserver/server.go:1151&quot;,&quot;msg&quot;:&quot;data-dir used by this member must be removed&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.959+0800&quot;,&quot;caller&quot;:&quot;etcdserver/server.go:2053&quot;,&quot;msg&quot;:&quot;stopped publish because server is stopped&quot;,&quot;local-member-id&quot;:&quot;af3fef14dc164ad9&quot;,&quot;local-member-attributes&quot;:&quot;&#123;Name:etcd-1 ClientURLs:[http://etcd-1.etcd-headless.etcd:2379 http://etcd.etcd:2379]&#125;&quot;,&quot;publish-timeout&quot;:&quot;25s&quot;,&quot;error&quot;:&quot;etcdserver: server stopped&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T22:47:11.962+0800&quot;,&quot;caller&quot;:&quot;rafthttp/peer.go:330&quot;,&quot;msg&quot;:&quot;stopping remote peer&quot;,&quot;remote-peer-id&quot;:&quot;432042ce92be9a79&quot;&#125;</span><br></pre></td></tr></table></figure><p>查看 etcd 集群成员状态：（这个命令无法得知 endpoint 的健康状态）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd exec -it etcd-0 -- bash</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl member list</span><br><span class="line">14105f2e4f2559a9, started, etcd-0, http://etcd-0.etcd-headless.etcd:2380, http://etcd-0.etcd-headless.etcd:2379,http://etcd.etcd:2379, false</span><br><span class="line">432042ce92be9a79, started, etcd-2, http://etcd-2.etcd-headless.etcd:2380, http://etcd-2.etcd-headless.etcd:2379,http://etcd.etcd:2379, false</span><br><span class="line">e3ff14c8562cf8c2, started, etcd-1, http://etcd-1.etcd-headless.etcd:2380, http://etcd-1.etcd-headless.etcd:2379,http://etcd.etcd:2379, false</span><br></pre></td></tr></table></figure><p>查看 etcd 集群 endpoint 健康状态：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl endpoint health --endpoints=etcd-0.etcd-headless.etcd:2380,etcd-1.etcd-headless.etcd:2380,etcd-2.etcd-headless.etcd:2380 -w table</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-07-31T22:58:52.131+0800&quot;,&quot;logger&quot;:&quot;client&quot;,&quot;caller&quot;:&quot;v3/retry_interceptor.go:62&quot;,&quot;msg&quot;:&quot;retrying of unary invoker failed&quot;,&quot;target&quot;:&quot;etcd-endpoints://0x400044aa80/etcd-1.etcd-headless.etcd:2380&quot;,&quot;attempt&quot;:0,&quot;error&quot;:&quot;rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error: desc = \&quot;transport: Error while dialing dial tcp 192.168.188.20:2380: connect: connection refused\&quot;&quot;&#125;</span><br><span class="line">+--------------------------------+--------+-------------+---------------------------+</span><br><span class="line">|            ENDPOINT            | HEALTH |    TOOK     |           ERROR           |</span><br><span class="line">+--------------------------------+--------+-------------+---------------------------+</span><br><span class="line">| etcd-0.etcd-headless.etcd:2380 |   true |   2.97674ms |                           |</span><br><span class="line">| etcd-2.etcd-headless.etcd:2380 |   true |   8.06479ms |                           |</span><br><span class="line">| etcd-1.etcd-headless.etcd:2380 |  false | 5.00261904s | context deadline exceeded |</span><br><span class="line">+--------------------------------+--------+-------------+---------------------------+</span><br><span class="line">Error: unhealthy cluster</span><br></pre></td></tr></table></figure><h2 id="恢复方案">恢复方案</h2><p>（1）将故障成员 etcd-1 从集群中移除</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd exec -it etcd-0 -- bash</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl endpoint health --endpoints=etcd-0.etcd-headless.etcd:2380,etcd-1.etcd-headless.etcd:2380,etcd-2.etcd-headless.etcd:2380</span><br><span class="line">&#123;&quot;level&quot;:&quot;warn&quot;,&quot;ts&quot;:&quot;2023-07-31T23:42:37.481+0800&quot;,&quot;logger&quot;:&quot;client&quot;,&quot;caller&quot;:&quot;v3/retry_interceptor.go:62&quot;,&quot;msg&quot;:&quot;retrying of unary invoker failed&quot;,&quot;target&quot;:&quot;etcd-endpoints://0x4000230000/etcd-1.etcd-headless.etcd:2380&quot;,&quot;attempt&quot;:0,&quot;error&quot;:&quot;rpc error: code = DeadlineExceeded desc = latest balancer error: last connection error: connection error: desc = \&quot;transport: Error while dialing dial tcp 192.168.188.20:2380: connect: connection refused\&quot;&quot;&#125;</span><br><span class="line">etcd-0.etcd-headless.etcd:2380 is healthy: successfully committed proposal: took = 4.34159ms</span><br><span class="line">etcd-2.etcd-headless.etcd:2380 is healthy: successfully committed proposal: took = 5.19394ms</span><br><span class="line">etcd-1.etcd-headless.etcd:2380 is unhealthy: failed to commit proposal: context deadline exceeded</span><br><span class="line">Error: unhealthy cluster</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl member list -w table</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br><span class="line">|        ID        | STATUS  |  NAME  |              PEER ADDRS               |                         CLIENT ADDRS                        | IS LEARNER |</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br><span class="line">| 14105f2e4f2559a9 | started | etcd-0 | http://etcd-0.etcd-headless.etcd:2380 | http://etcd-0.etcd-headless.etcd:2379,http://etcd.etcd:2379 |      false |</span><br><span class="line">| 432042ce92be9a79 | started | etcd-2 | http://etcd-2.etcd-headless.etcd:2380 | http://etcd-2.etcd-headless.etcd:2379,http://etcd.etcd:2379 |      false |</span><br><span class="line">| e3ff14c8562cf8c2 | started | etcd-1 | http://etcd-1.etcd-headless.etcd:2380 | http://etcd-1.etcd-headless.etcd:2379,http://etcd.etcd:2379 |      false |</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl member remove e3ff14c8562cf8c2</span><br><span class="line">Member e3ff14c8562cf8c2 removed from cluster d96b6d5811544d30</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl member list -w table</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br><span class="line">|        ID        | STATUS  |  NAME  |              PEER ADDRS               |                         CLIENT ADDRS                        | IS LEARNER |</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br><span class="line">| 14105f2e4f2559a9 | started | etcd-0 | http://etcd-0.etcd-headless.etcd:2380 | http://etcd-0.etcd-headless.etcd:2379,http://etcd.etcd:2379 |      false |</span><br><span class="line">| 432042ce92be9a79 | started | etcd-2 | http://etcd-2.etcd-headless.etcd:2380 | http://etcd-2.etcd-headless.etcd:2379,http://etcd.etcd:2379 |      false |</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br></pre></td></tr></table></figure><p>（2）将 etcd statefulsets 缩放为 0</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd scale statefulset etcd --replicas=0</span><br><span class="line">statefulset.apps/etcd scaled</span><br><span class="line">[root@cnode1 ~]# kubectl -n etcd get statefulsets.apps etcd</span><br><span class="line">NAME   READY   AGE</span><br><span class="line">etcd   0/0     30d</span><br></pre></td></tr></table></figure><p>（3）删除故障成员 etcd-1 绑定的 pv 的数据目录</p><ul><li><p>获取 etcd-1 的 pv</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd get pvc</span><br><span class="line">NAME                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS    AGE</span><br><span class="line">data-etcd-0          Bound    pvc-2a2f2a25-69b3-41ab-a21d-a5aa3d05277d   10Gi       RWO            local-storage   30d</span><br><span class="line">data-etcd-1          Bound    pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20   10Gi       RWO            local-storage   4h10m</span><br><span class="line">data-etcd-2          Bound    pvc-6da8bcd7-cd2a-43fc-9849-7e1c2870f193   10Gi       RWO            local-storage   30d</span><br></pre></td></tr></table></figure></li><li><p>获取 etcd-1 pv 所在的节点</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl get pv pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20 -o yaml | grep nodeAffinity -A 7</span><br><span class="line">  nodeAffinity:</span><br><span class="line">    required:</span><br><span class="line">      nodeSelectorTerms:</span><br><span class="line">      - matchExpressions:</span><br><span class="line">        - key: kubernetes.io/hostname</span><br><span class="line">          operator: In</span><br><span class="line">          values:</span><br><span class="line">          - cnode3</span><br></pre></td></tr></table></figure></li><li><p>获取 etcd-1 pv 的数据目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl get pv pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20 -o yaml | grep hostPath -A 2</span><br><span class="line">  hostPath:</span><br><span class="line">    path: /data/local-path-provisioner/pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20_etcd_data-etcd-1</span><br><span class="line">    type: DirectoryOrCreate</span><br></pre></td></tr></table></figure></li><li><p>进入节点 cnode3 后台</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh root@cnode3</span><br></pre></td></tr></table></figure></li><li><p>清除 etcd-1 pv 的数据目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# rm -rf /data/local-path-provisioner/pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20_etcd_data-etcd-1/data/</span><br><span class="line">[root@cnode1 ~]# ls /data/local-path-provisioner/pvc-4832f8c4-0827-4992-9e39-185f9c0f8e20_etcd_data-etcd-1/</span><br><span class="line">[root@cnode1 ~]#</span><br></pre></td></tr></table></figure></li></ul><p>（4）修改 statefulsets 中 ETCD_INITIAL_CLUSTER_STATE 配置的值为 "existing"</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd edit statefulsets.apps etcd</span><br><span class="line">statefulset.apps/etcd edited</span><br></pre></td></tr></table></figure><p>修改内容如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="attr">name:</span> <span class="string">ETCD_INITIAL_CLUSTER_STATE</span></span><br><span class="line">  <span class="attr">value:</span> <span class="string">existing</span></span><br></pre></td></tr></table></figure><p>（5）将 etcd statefulsets 恢复为原来的副本数</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd scale statefulset etcd --replicas=3</span><br><span class="line">statefulset.apps/etcd scaled</span><br></pre></td></tr></table></figure><p>（6）查看 etcd-1 的日志</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd logs -f etcd-1</span><br><span class="line">...</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T23:48:36.065+0800&quot;,&quot;caller&quot;:&quot;flags/flag.go:113&quot;,&quot;msg&quot;:&quot;recognized and used environment variable&quot;,&quot;variable-name&quot;:&quot;ETCD_INITIAL_CLUSTER&quot;,&quot;variable-value&quot;:&quot;etcd-0=http://etcd-0.etcd-headless.etcd:2380,etcd-1=http://etcd-1.etcd-headless.etcd:2380,etcd-2=http://etcd-2.etcd-headless.etcd:2380&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T23:48:36.065+0800&quot;,&quot;caller&quot;:&quot;flags/flag.go:113&quot;,&quot;msg&quot;:&quot;recognized and used environment variable&quot;,&quot;variable-name&quot;:&quot;ETCD_INITIAL_CLUSTER_STATE&quot;,&quot;variable-value&quot;:&quot;existing&quot;&#125;</span><br><span class="line">...</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T23:48:36.430+0800&quot;,&quot;caller&quot;:&quot;etcdserver/server.go:2042&quot;,&quot;msg&quot;:&quot;published local member to cluster through raft&quot;,&quot;local-member-id&quot;:&quot;1ea060417ff0fbae&quot;,&quot;local-member-attributes&quot;:&quot;&#123;Name:etcd-1 ClientURLs:[http://etcd-1.etcd-headless.etcd:2379 http://etcd.etcd:2379]&#125;&quot;,&quot;request-path&quot;:&quot;/0/members/1ea060417ff0fbae/attributes&quot;,&quot;cluster-id&quot;:&quot;d96b6d5811544d30&quot;,&quot;publish-timeout&quot;:&quot;25s&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T23:48:36.430+0800&quot;,&quot;caller&quot;:&quot;embed/serve.go:98&quot;,&quot;msg&quot;:&quot;ready to serve client requests&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T23:48:36.430+0800&quot;,&quot;caller&quot;:&quot;etcdmain/main.go:44&quot;,&quot;msg&quot;:&quot;notifying init daemon&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T23:48:36.430+0800&quot;,&quot;caller&quot;:&quot;etcdmain/main.go:50&quot;,&quot;msg&quot;:&quot;successfully notified init daemon&quot;&#125;</span><br><span class="line">&#123;&quot;level&quot;:&quot;info&quot;,&quot;ts&quot;:&quot;2023-07-31T23:48:36.431+0800&quot;,&quot;caller&quot;:&quot;embed/serve.go:140&quot;,&quot;msg&quot;:&quot;serving client traffic insecurely; this is strongly discouraged!&quot;,&quot;address&quot;:&quot;[::]:2379&quot;&#125;</span><br></pre></td></tr></table></figure><p>（7）查看 etcd statefulsets 状态</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd get statefulsets.apps etcd</span><br><span class="line">NAME   READY   AGE</span><br><span class="line">etcd   3/3     30d</span><br><span class="line">[root@cnode1 ~]# kubectl -n etcd get pod</span><br><span class="line">NAME            READY   STATUS      RESTARTS       AGE</span><br><span class="line">etcd-0          1/1     Running     0              6m39s</span><br><span class="line">etcd-1          1/1     Running     0              6m39s</span><br><span class="line">etcd-2          1/1     Running     0              6m39s</span><br></pre></td></tr></table></figure><p>（8）检查 etcd 集群 endpoint 健康状态</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd exec -it etcd-0 -- bash</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl endpoint health --endpoints=etcd-0.etcd-headless.etcd:2380,etcd-1.etcd-headless.etcd:2380,etcd-2.etcd-headless.etcd:2380 -w table</span><br><span class="line">+--------------------------------+--------+-----------+-------+</span><br><span class="line">|            ENDPOINT            | HEALTH |   TOOK    | ERROR |</span><br><span class="line">+--------------------------------+--------+-----------+-------+</span><br><span class="line">| etcd-0.etcd-headless.etcd:2380 |   true | 5.09933ms |       |</span><br><span class="line">| etcd-1.etcd-headless.etcd:2380 |   true | 5.97046ms |       |</span><br><span class="line">| etcd-2.etcd-headless.etcd:2380 |   true | 5.37909ms |       |</span><br><span class="line">+--------------------------------+--------+-----------+-------+</span><br></pre></td></tr></table></figure><p>（9）查看 etcd 集群成员状态</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd exec -it etcd-0 -- bash</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl member list -w table</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br><span class="line">|        ID        | STATUS  |  NAME  |              PEER ADDRS               |                         CLIENT ADDRS                        | IS LEARNER |</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br><span class="line">| 14105f2e4f2559a9 | started | etcd-0 | http://etcd-0.etcd-headless.etcd:2380 | http://etcd-0.etcd-headless.etcd:2379,http://etcd.etcd:2379 |      false |</span><br><span class="line">| 1ea060417ff0fbae | started | etcd-1 | http://etcd-1.etcd-headless.etcd:2380 | http://etcd-1.etcd-headless.etcd:2379,http://etcd.etcd:2379 |      false |</span><br><span class="line">| 432042ce92be9a79 | started | etcd-2 | http://etcd-2.etcd-headless.etcd:2380 | http://etcd-2.etcd-headless.etcd:2379,http://etcd.etcd:2379 |      false |</span><br><span class="line">+------------------+---------+--------+---------------------------------------+-------------------------------------------------------------+------------+</span><br></pre></td></tr></table></figure><p>（10）修改 statefulsets 中 ETCD_INITIAL_CLUSTER_STATE 配置的值恢复为 "new"（可选）</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd edit statefulsets.apps etcd</span><br><span class="line">statefulset.apps/etcd edited</span><br></pre></td></tr></table></figure><p>修改内容如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> <span class="attr">name:</span> <span class="string">ETCD_INITIAL_CLUSTER_STATE</span></span><br><span class="line">  <span class="attr">value:</span> <span class="string">new</span></span><br></pre></td></tr></table></figure><p>等待 statefulsets 重启完成：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd get statefulsets.apps etcd</span><br><span class="line">NAME   READY   AGE</span><br><span class="line">etcd   3/3     30d</span><br><span class="line">[root@cnode1 ~]# kubectl -n etcd get pod</span><br><span class="line">NAME            READY   STATUS      RESTARTS       AGE</span><br><span class="line">etcd-0          1/1     Running     0              2m2s</span><br><span class="line">etcd-1          1/1     Running     0              3m5s</span><br><span class="line">etcd-2          1/1     Running     0              4m8s</span><br></pre></td></tr></table></figure><p>（11）验证数据是否完整</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 ~]# kubectl -n etcd exec -it etcd-0 -- bash</span><br><span class="line">I have no name!@etcd-0:/opt/bitnami/etcd$ bin/etcdctl get /hello</span><br><span class="line">/hello</span><br><span class="line">world</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了在使用 bitnami/etcd 部署的集群中，如何恢复故障成员的方法。首先在处理前，往 etcd 集群中添加一些数据，以验证恢复方案不会导致集群数据丢失。然后，通过删除 etcd-1 pv 的数据目录来模拟 etcd-1 成员故障，并查看 etcd-1 pod 日志、etcd 集群成员状态以及 endpoint 健康状态来分析故障原因。接着，本文提供了一套恢复方案，包括将故障成员 etcd-1 从集群中移除、将 etcd statefulsets 缩放为 0、删除故障成员 etcd-1 绑定的 pv 的数据目录、修改 statefulsets 中 ETCD_INITIAL_CLUSTER_STATE 配置的值为 &quot;existing&quot;、将 etcd statefulsets 恢复为原来的副本数以及检查 etcd 集群 endpoint 健康状态等步骤。最后，验证数据是否完整。&lt;/p&gt;</summary>
    
    
    
    <category term="middleware" scheme="https://www.wylu.me/categories/middleware/"/>
    
    <category term="distributed coordination" scheme="https://www.wylu.me/categories/middleware/distributed-coordination/"/>
    
    <category term="etcd" scheme="https://www.wylu.me/categories/middleware/distributed-coordination/etcd/"/>
    
    
    <category term="etcd" scheme="https://www.wylu.me/tags/etcd/"/>
    
    <category term="bitnami" scheme="https://www.wylu.me/tags/bitnami/"/>
    
  </entry>
  
  <entry>
    <title>nmcli 网络管理工具</title>
    <link href="https://www.wylu.me/posts/697568dc/"/>
    <id>https://www.wylu.me/posts/697568dc/</id>
    <published>2023-07-30T06:42:05.000Z</published>
    <updated>2023-07-30T06:45:53.705Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了 nmcli (Network Manager Command Line Interface) 网络管理工具，它是 NetworkManager 的命令行工具，用于在 Linux 系统上管理网络连接。nmcli 允许用户通过命令行界面管理和配置网络连接，包括无线网络、以太网、VPN 等。本文详细介绍了 nmcli 的安装方法和各种命令，包括选项、通用命令、网络控制命令、设备管理命令、连接管理命令、无线传输控制命令和活动监视器。此外，本文还提供了 nmcli 的示例，供用户参考。nmcli 是一个功能强大且灵活的工具，常用于服务器管理和命令行环境下的网络配置。</p><span id="more"></span><h1 id="nmcli-网络管理工具">nmcli 网络管理工具</h1><h2 id="nmcli-简介">nmcli 简介</h2><p><a href="https://networkmanager.dev/" class="uri">https://networkmanager.dev/</a></p><p>nmcli (Network Manager Command Line Interface) 是 NetworkManager 的命令行工具。NetworkManager 是一个在 Linux 系统上管理网络连接的守护进程。nmcli 允许用户通过命令行界面管理和配置网络连接，包括无线网络、以太网、VPN 等。使用 nmcli，您可以列出可用的网络连接、连接到特定的网络、配置网络设置、查看网络状态等。它是一个功能强大且灵活的工具，常用于服务器管理和命令行环境下的网络配置。</p><blockquote><p>NetworkManager 是 2004 年 RedHat 启动的项目，皆在能够让 Linux 用户更轻松的处理现代网络需求，尤其是无线网络，能够自动发现网卡并配置 IP 地址。</p><p>RHEL7 上同时支持 network.service 和 NetworkManager.service（简称 NM）。默认情况下这两个服务都有开启，但是因为 NetworkManager.service 当时的兼容性不好，有时会造成 IP 地址无法生效或网络不通问题，大部分人都会将其关闭 NM。</p><p>但是在 RHEL 8/CentOS 8上已废弃 network.service（默认不安装），只能通过 NetworkManager 进行网络配置。</p><p>NetworkManager 主要管理两个对象：Connection（网卡连接配置） 和 Device（网卡设备）。</p><p>在 RHEL 8/CentOS 8有三种方法配置网络：</p><ul><li>通过 <code>nmcli connection add</code> 命令配置，会自动生成 ifcfg 文件；</li><li>手动配置 ifcfg 文件，通过 <code>nmcli connection reload</code> 来加载生效；</li><li>手动配置 ifcfg 文件，通过传统 network.service 来加载生效；</li></ul></blockquote><h2 id="nmcli-安装">nmcli 安装</h2><p>nmcli 实用工具是由 NetworkManager 包提供，有关详情，请参阅 NetworkManager(8)。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# man 8 NetworkManager</span><br></pre></td></tr></table></figure><p>在 CentOS 7 / CentOS 8 中，默认已安装：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# cat /etc/redhat-release</span><br><span class="line">Rocky Linux release 8.6 (Green Obsidian)</span><br><span class="line">[root@localhost ~]# yum install -y NetworkManager</span><br><span class="line">Last metadata expiration check: 0:03:25 ago on Sun 30 Jul 2023 06:28:20 AM CST.</span><br><span class="line">Package NetworkManager-1:1.36.0-4.el8.x86_64 is already installed.</span><br><span class="line">...</span><br><span class="line">[root@localhost ~]# nmcli --version</span><br><span class="line">nmcli tool, version 1.40.16-3.el8_8</span><br></pre></td></tr></table></figure><h2 id="nmcli-命令">nmcli 命令</h2><p><a href="https://networkmanager.dev/docs/api/latest/nmcli.html" class="uri">https://networkmanager.dev/docs/api/latest/nmcli.html</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli</span><br><span class="line">agent       connection  device      general     help        monitor     networking  radio</span><br></pre></td></tr></table></figure><p>nmcli 有 8 个子命令，这里重点介绍 device、 connection。</p><h3 id="选项options">选项（Options）</h3><ul><li><p><code>-f | --fields &#123; field1,field2... | all | common &#125;</code></p><p>该选项用于指定应打印哪些字段（列名）。特定命令的有效字段名称有所不同。通过向 <code>--fields</code> 选项提供无效值来列出可用字段。all 用于打印命令的所有有效字段值。common 用于打印命令的公共字段值。</p><p>如果省略，则默认为 common。</p></li><li><p><code>-g | --get-values &#123; field1,field2... | all | common &#125;</code></p><p>此选项用于打印特定字段中的值。它基本上是 <code>--mode tabular --terse --fields</code> 的快捷方式，是检索特定字段值的便捷方法。每行打印一个值，不带标题。</p><p>如果指定了节（section）而不是字段，则将打印节名称，后跟属于该节的字段的冒号分隔值，所有这些都在同一行上。</p></li><li><p><code>-m | --mode &#123; tabular | multiline &#125;</code></p><p>在表格和多行输出之间切换：</p><ul><li>tabular：输出是一个表，其中每行描述一个条目。列定义条目的特定属性。</li><li>multiline：每个条目由多行组成，每个属性独占一行。这些值以属性名称为前缀。</li></ul><p>如果省略，则大多数命令的默认值是 tabular 形式。 对于产生更多结构化信息的命令，无法在单行上显示，默认为 multiline。 目前，它们是：</p><ul><li><code>nmcli connection show ID</code></li><li><code>nmcli device show</code></li></ul></li><li><p><code>-p | --pretty</code></p><p>美化输出。这使得 nmcli 产生人类易于阅读的输出，即值对齐、打印标题等。</p></li><li><p><code>-t | --terse</code></p><p>简洁输出。该模式是为计算机（脚本）处理而设计的。</p></li><li><p><code>-w | --wait seconds</code></p><p>此选项设置 nmcli 将等待 NetworkManager 完成操作的超时时间。它对于可能需要较长时间才能完成的命令特别有用，例如 连接激活。</p><p>指定值 0 指示 nmcli 不等待而是立即退出并显示成功状态。默认值取决于执行的命令。</p></li></ul><h3 id="通用命令general-commands">通用命令（General Commands）</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nmcli general &#123; status | hostname | permissions | logging | reload &#125; [ARGUMENTS...]</span><br></pre></td></tr></table></figure><p>使用此命令显示 NetworkManager 状态和权限。 您还可以获取和更改系统主机名以及 NetworkManager 日志记录级别和域。</p><h4 id="status">status</h4><p>显示 NetworkManager 的整体状态。当没有为 nmcli 常规提供其他命令时，这是默认操作。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli general status</span><br><span class="line">STATE      CONNECTIVITY  WIFI-HW  WIFI     WWAN-HW  WWAN</span><br><span class="line">connected  full          missing  enabled  missing  enabled</span><br></pre></td></tr></table></figure><h4 id="hostname">hostname</h4><p><code>hostname [hostname]</code></p><p>获取和更改系统主机名。如果没有参数，这将打印当前配置的主机名。当您传递主机名时，它将被移交给 NetworkManager 以设置为新的系统主机名。</p><blockquote><p>Note that the term "system" hostname may also be referred to as "persistent" or "static" by other programs or tools. The hostname is stored in <code>/etc/hostname</code> file in most distributions. For example, systemd-hostnamed service uses the term "static" hostname and it only reads the <code>/etc/hostname</code> file when it starts.</p></blockquote><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看主机名</span></span><br><span class="line">[root@localhost ~]# nmcli general hostname</span><br><span class="line">localhost.localdomain</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">修改主机名</span></span><br><span class="line">[root@localhost ~]# nmcli general hostname rocky</span><br><span class="line">[root@localhost ~]# nmcli general hostname</span><br><span class="line">rocky</span><br></pre></td></tr></table></figure><h4 id="permissions">permissions</h4><p>显示调用者（当前用户）对 NetworkManager 提供的各种经过身份验证的操作拥有的权限，例如启用和禁用网络、更改 Wi-Fi 和 WWAN 状态、修改连接等。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli general permissions</span><br><span class="line">PERMISSION                                                        VALUE</span><br><span class="line">org.freedesktop.NetworkManager.checkpoint-rollback                yes</span><br><span class="line">org.freedesktop.NetworkManager.enable-disable-connectivity-check  yes</span><br><span class="line">org.freedesktop.NetworkManager.enable-disable-network             yes</span><br><span class="line">org.freedesktop.NetworkManager.enable-disable-statistics          yes</span><br><span class="line">org.freedesktop.NetworkManager.enable-disable-wifi                yes</span><br><span class="line">org.freedesktop.NetworkManager.enable-disable-wimax               yes</span><br><span class="line">org.freedesktop.NetworkManager.enable-disable-wwan                yes</span><br><span class="line">org.freedesktop.NetworkManager.network-control                    yes</span><br><span class="line">org.freedesktop.NetworkManager.reload                             yes</span><br><span class="line">org.freedesktop.NetworkManager.settings.modify.global-dns         yes</span><br><span class="line">org.freedesktop.NetworkManager.settings.modify.hostname           yes</span><br><span class="line">org.freedesktop.NetworkManager.settings.modify.own                yes</span><br><span class="line">org.freedesktop.NetworkManager.settings.modify.system             yes</span><br><span class="line">org.freedesktop.NetworkManager.sleep-wake                         yes</span><br><span class="line">org.freedesktop.NetworkManager.wifi.scan                          yes</span><br><span class="line">org.freedesktop.NetworkManager.wifi.share.open                    yes</span><br><span class="line">org.freedesktop.NetworkManager.wifi.share.protected               yes</span><br></pre></td></tr></table></figure><h4 id="logging">logging</h4><p><code>logging [level level] [domains domains...]</code></p><p>获取和更改 NetworkManager 日志记录级别和域。如果没有任何参数，则会显示当前日志记录级别和域。为了更改日志记录状态，请提供 <code>level</code> 和/或 <code>domain</code> 参数。有关可用 level 和 domain 值，请参阅 <a href="https://networkmanager.dev/docs/api/latest/NetworkManager.conf.html">NetworkManager.conf(5)</a>。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli general logging</span><br><span class="line">LEVEL  DOMAINS</span><br><span class="line">INFO   PLATFORM,RFKILL,ETHER,WIFI,BT,MB,DHCP4,DHCP6,PPP,IP4,IP6,AUTOIP4,DNS,VPN,SHARING,SUPPLICANT,AGENTS,SETTINGS,SUSPEND,CORE,DEVICE,OLPC,INFINIBAND,FIREWALL,ADSL,BOND,VLAN,BRIDGE,TEAM,CONCHECK,DCB,DISPATCH,AUDIT,SYSTEMD,PROXY</span><br></pre></td></tr></table></figure><h4 id="reload">reload</h4><p><code>reload [flags...]</code></p><p>重新加载 NetworkManager 的配置并执行某些更新，例如刷新缓存或将外部状态重写到磁盘。这类似于向 NetworkManager 发送 SIGHUP，但它允许通过 <code>flags</code> 参数对重新加载的内容进行更细粒度的控制。它还允许通过 PolicyKit 进行非 root 访问，并且与信号相反，它是同步的。可用的 <code>flags</code> 有：</p><ul><li><code>conf</code>：从磁盘重新加载 NetworkManager.conf 配置。请注意，这不包括连接，可以通过 <strong>nmcli connection reload</strong> 来重新加载连接。</li><li><code>dns-rc</code>：更新 DNS 配置，这通常涉及重新编写 /etc/resolv.conf。这相当于向 NetworkManager 进程发送 SIGUSR1 信号。</li><li><code>dns-full</code>：重新启动 DNS 插件。例如，当使用 dnsmasq 插件时，这非常有用，该插件使用 /etc/NetworkManager/dnsmasq.d 中的附加配置。如果编辑这些文件，则可以重新启动 DNS 插件。此操作会很快中断名称解析。</li></ul><p>如果没有指定 <code>flags</code>，所有支持的内容都会重新加载，这与发送 SIGHUP 相同。 有关信号的更多详细信息，请参阅 NetworkManager(8)。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli general reload conf</span><br></pre></td></tr></table></figure><h3 id="网络控制命令networking-control-commands">网络控制命令（Networking Control Commands）</h3><p>查询 NetworkManager 网络状态，启用和禁用网络。</p><h4 id="on-off">on, off</h4><p>通过 NetworkManager 启用或禁用网络控制。当网络被禁用时，由 NetworkManager 管理的所有接口都将被停用。</p><p>禁用网络控制：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE      CONNECTION</span><br><span class="line">ens32   ethernet  connected  ens32</span><br><span class="line">ens33   ethernet  connected  ens33</span><br><span class="line">ens34   ethernet  connected  ens34</span><br><span class="line">lo      loopback  unmanaged  --</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">ens34  84377dc9-3ec0-4573-925e-d3678801da6d  ethernet  ens34</span><br><span class="line">[root@localhost ~]# nmcli networking connectivity check</span><br><span class="line">full</span><br><span class="line">[root@localhost ~]# nmcli networking off</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  --</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  --</span><br><span class="line">ens34  84377dc9-3ec0-4573-925e-d3678801da6d  ethernet  --</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE      CONNECTION</span><br><span class="line">ens32   ethernet  unmanaged  --</span><br><span class="line">ens33   ethernet  unmanaged  --</span><br><span class="line">ens34   ethernet  unmanaged  --</span><br><span class="line">lo      loopback  unmanaged  --</span><br></pre></td></tr></table></figure><p>启用网络控制：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli networking on</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">ens34  84377dc9-3ec0-4573-925e-d3678801da6d  ethernet  ens34</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE      CONNECTION</span><br><span class="line">ens32   ethernet  connected  ens32</span><br><span class="line">ens33   ethernet  connected  ens33</span><br><span class="line">ens34   ethernet  connected  ens34</span><br><span class="line">lo      loopback  unmanaged  --</span><br></pre></td></tr></table></figure><h4 id="connectivity">connectivity</h4><p><code>connectivity [check]</code></p><p>获取网络连接状态。可选的 <code>check</code> 参数告诉 NetworkManager 重新检查连接，否则将显示最新的已知连接状态而不重新检查。</p><p>可能的状态有：</p><ul><li><code>none</code>：主机未连接到任何网络。</li><li><code>portal</code>：主机位于强制门户后面，无法访问完整的互联网。</li><li><code>limited</code>：主机已连接到网络，但无法访问 Internet。</li><li><code>full</code>：主机已连接到网络并具有对 Internet 的完全访问权限。</li><li><code>unknown</code>：无法查到连接状态。</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli networking connectivity</span><br><span class="line">full</span><br><span class="line">[root@localhost ~]# nmcli networking connectivity check</span><br><span class="line">full</span><br></pre></td></tr></table></figure><h3 id="设备管理命令device-management-commands">设备管理命令（Device Management Commands）</h3><p>显示和管理网络接口。</p><p><code>nmcli device</code> 按 tab 两下可以获取 device 后面的参数：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device</span><br><span class="line">checkpoint  delete      down        lldp        monitor     set         status      wifi</span><br><span class="line">connect     disconnect  help        modify      reapply     show        up</span><br></pre></td></tr></table></figure><p>网卡属于物理硬件，NetworkManager 属于软件层面，如何通过软件层面给网卡绑定 IP 地址等信息？中间有一个核心：kernel。Linux 应用层 app 通过 kernel 来驱使底层物理硬件运行，同时必须要明确一点：软件应用没办法改变物理底层信息。</p><p>例如我们给 eth0 配置 IP 地址为 192.168.1.1。实质上并不是用刀在 eth0 网卡上刻上 192.168.1.1，实际上是 kernel 获取 eth0 网卡总线，同时将该总线命名（方便人为区分），然后 NetworkManager 告诉 kernel 00:00.0（假设 eth0）的 IP 地址是 192.168.1.1/24，那么以后访问 192.168.1.1 就是在访问 00:00.0，kernel 在中间做了一次转换或者说是绑定。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# lshw -class net -businfo</span><br><span class="line">Bus info          Device      Class      Description</span><br><span class="line">====================================================</span><br><span class="line">pci@0000:02:00.0  ens32       network    82545EM Gigabit Ethernet Controller (Copper)</span><br><span class="line">pci@0000:02:01.0  ens33       network    82545EM Gigabit Ethernet Controller (Copper)</span><br><span class="line">pci@0000:02:02.0  ens34       network    82545EM Gigabit Ethernet Controller (Copper)</span><br><span class="line">[root@localhost ~]# lspci</span><br><span class="line">00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)</span><br><span class="line">00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)</span><br><span class="line">00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)</span><br><span class="line">00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)</span><br><span class="line">00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)</span><br><span class="line">00:07.7 System peripheral: VMware Virtual Machine Communication Interface (rev 10)</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>00:00.0 就是总线。</p><h4 id="status-1">status</h4><p>打印设备状态。如果未向 <code>nmcli device</code> 指定子命令，则这是默认操作。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br></pre></td></tr></table></figure><ul><li><p>DEVICE：表示网卡的名称（kernel 获取的标识）</p></li><li><p>TYPE：表示网卡的类型</p></li><li><p>STATE：表示网卡与配置文件的连接状态</p><ul><li>connected：表示 NetworkManager 接管</li><li>disconnected：表示使用 NetworkManager 管理</li><li>unmanaged：表示不使用 NetworkManager 管理</li></ul></li><li><p>CONNECTION：网卡对应的 connection 配置文件名称</p><p>connection 配置文件名可以通过 "nmcli connection modify uuid xxxxxxxxx con-name xxx" 来修改：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">修改 connection 配置文件名</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[root@localhost ~]<span class="comment"># nmcli connection modify ens32 con-name eth0</span></span></span><br><span class="line">[root@localhost ~]# nmcli connection modify uuid e1af3066-1f0e-44a5-a4a3-c0174cf77a7b con-name eth0</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">eth0   e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     eth0</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br></pre></td></tr></table></figure><p>这里演示的就是将网卡 ens32 的配置文件由 <code>nmcli device status</code> 看到的 "ens32" 名称修改成 "eth0"（网卡的配置文件通过 <code>nmcli connection</code> 来修改的）。</p><p><code>nmcli connection show</code> 可以看到各个配置文件对应的唯一识别 uuid，<code>nmcli connection show</code> 输出中 NAME 是配置文件名，后面的 DEVICE 是配置文件对应的网卡名称，配置文件名 NAME 是可以修改的，而 DEVICE 是 kernel 标识的，但是也可以通过其他手段来修改。</p></li></ul><h4 id="show">show</h4><p><code>show [ifname]</code></p><p>（1）若不加任何参数，则会显示所有的网卡的信息。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br></pre></td></tr></table></figure><p>（2）若加上 DEVICE 名称，则显示 DEVICE 对应网卡的所有信息。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device show ens34</span><br><span class="line">GENERAL.DEVICE:                         ens34</span><br><span class="line">GENERAL.TYPE:                           ethernet</span><br><span class="line">GENERAL.HWADDR:                         00:0C:29:66:3F:D9</span><br><span class="line">GENERAL.MTU:                            1500</span><br><span class="line">GENERAL.STATE:                          30 (disconnected)</span><br><span class="line">GENERAL.CONNECTION:                     --</span><br><span class="line">GENERAL.CON-PATH:                       --</span><br><span class="line">WIRED-PROPERTIES.CARRIER:               on</span><br><span class="line">IP4.GATEWAY:                            --</span><br><span class="line">IP6.GATEWAY:                            --</span><br></pre></td></tr></table></figure><p>因为网卡 ens34 没有关联 connection，所以显示的信息相对少。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device show ens32</span><br><span class="line">GENERAL.DEVICE:                         ens32</span><br><span class="line">GENERAL.TYPE:                           ethernet</span><br><span class="line">GENERAL.HWADDR:                         00:0C:29:66:3F:C5</span><br><span class="line">GENERAL.MTU:                            1500</span><br><span class="line">GENERAL.STATE:                          100 (connected)</span><br><span class="line">GENERAL.CONNECTION:                     ens32</span><br><span class="line">GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/4</span><br><span class="line">WIRED-PROPERTIES.CARRIER:               on</span><br><span class="line">IP4.ADDRESS[1]:                         192.168.0.25/24</span><br><span class="line">IP4.GATEWAY:                            192.168.0.1</span><br><span class="line">IP4.ROUTE[1]:                           dst = 192.168.0.0/24, nh = 0.0.0.0, mt = 100</span><br><span class="line">IP4.ROUTE[2]:                           dst = 0.0.0.0/0, nh = 192.168.0.1, mt = 100</span><br><span class="line">IP4.DNS[1]:                             114.114.114.114</span><br><span class="line">IP4.DNS[2]:                             8.8.8.8</span><br><span class="line">IP6.ADDRESS[1]:                         fe80::20c:29ff:fe66:3fc5/64</span><br><span class="line">IP6.GATEWAY:                            --</span><br><span class="line">IP6.ROUTE[1]:                           dst = fe80::/64, nh = ::, mt = 1024</span><br></pre></td></tr></table></figure><p>因为网卡 ens32 关联了 connection 配置文件，可以看到 DEVICE 网卡名 ens32，TYPE 使用的网络类型为 ethernet， HWADDR 硬件地址 00:0C:29:66:3F:C5，还有 MTU、STATE（连接状态，connected）、CONNECTION ens32，这与 nmcli device status 看到的基本一致，接下来的 IP4.ADDRESS、IP4.GATEWAY 等等都是写到配置文件中的内容。</p><h4 id="delete">delete</h4><p><code>delete ifname...</code></p><p>删除设备。该命令从系统中删除该接口。需要注意的是这仅适用于软件设备（如网桥、虚拟网卡），物理网卡等硬件设备是无法通过该命令删除的，不可能说 <code>nmcli device delete ens32</code> 就会物理地将网卡在计算机上拿走了。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">尝试删除硬件设备</span></span><br><span class="line">[root@localhost ~]# nmcli device delete ens32</span><br><span class="line">Error: Device &#x27;ens32&#x27; (/org/freedesktop/NetworkManager/Devices/2) deletion failed: This device is not a software device or is not realized</span><br><span class="line">Error: not all devices deleted.</span><br></pre></td></tr></table></figure><h4 id="disconnect">disconnect</h4><p><code>disconnect ifname...</code></p><p>down 命令的别名。1.34.0 版本之前不支持。</p><p>断开网卡与 connection 配置文件之间的关联。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br><span class="line">[root@localhost ~]# nmcli device disconnect ens33</span><br><span class="line">Device &#x27;ens33&#x27; successfully disconnected.</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  disconnected  --</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br></pre></td></tr></table></figure><h4 id="connect">connect</h4><p>将网卡与 connection 配置文件之间关联起来。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  disconnected  --</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br><span class="line">[root@localhost ~]# nmcli device connect ens33</span><br><span class="line">Device &#x27;ens33&#x27; successfully activated with &#x27;51b34523-8ea7-48f2-84c0-11d3f9aa828f&#x27;.</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br></pre></td></tr></table></figure><h4 id="monitor">monitor</h4><p>监控设备活动。每当指定设备更改状态时，此命令都会打印一行。</p><p>在未指定接口的情况下将监视所有设备。当所有指定的设备消失时，监视器终止。如果要监视添加的设备，请考虑使用带有 <code>nmcli monitor</code> 命令的全局监视器。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device monitor ens32</span><br></pre></td></tr></table></figure><h3 id="连接管理命令connection-management-commands">连接管理命令（Connection Management Commands）</h3><p>NetworkManager 将所有网络配置存储为 "connections"，它们是描述如何创建或连接到网络的数据集合（第 2 层详细信息、IP 寻址等）。当设备使用该连接的配置来创建或连接到网络时，该连接处于 "active" 状态。一个设备可能有多个连接，但在任何给定时间，该设备上只能有一个处于活动状态。其余连接可用于允许在不同网络和配置之间快速切换。</p><p>考虑一台通常连接到启用 DHCP 的网络，但有时连接到使用静态 IP 寻址的测试网络的计算机。 无需在每次网络更改时手动重新配置 eth0，这些设置可以保存为两个都适用于 eth0 的连接，一个用于 DHCP（称为 default），另一个具有静态寻址详细信息（称为 testing）。 当连接到启用 DHCP 的网络时，用户将运行 <strong>nmcli con up default</strong>，而当连接到静态网络时，用户将运行 <strong>nmcli con up testing</strong>。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli connection</span><br><span class="line">add      delete   edit     help     load     modify   reload   up</span><br><span class="line">clone    down     export   import   migrate  monitor  show</span><br></pre></td></tr></table></figure><p>nmcli connection 主要用来处理 connection 配置文件信息。</p><blockquote><p>The connection is identified by its name, UUID or D-Bus path. If <em><code>ID</code></em> is ambiguous, a keyword <code>id</code>, <code>uuid</code>, <code>path</code> or <code>apath</code> can be used.</p><p><code>id</code>, <code>uuid</code>, <code>path</code> and <code>apath</code> keywords can be used if <em><code>ID</code></em> is ambiguous. Optional <em><code>ID</code></em>-specifying keywords are:</p><table><colgroup><col style="width: 10%" /><col style="width: 89%" /></colgroup><thead><tr class="header"><th><code>id</code></th><th>the <em><code>ID</code></em> denotes a connection name.</th></tr></thead><tbody><tr class="odd"><td><code>uuid</code></td><td>the <em><code>ID</code></em> denotes a connection UUID.</td></tr><tr class="even"><td><code>path</code></td><td>the <em><code>ID</code></em> denotes a D-Bus static connection path in the format of /org/freedesktop/NetworkManager/Settings/<em><code>num</code></em> or just <em><code>num</code></em>.</td></tr><tr class="odd"><td><code>apath</code></td><td>the <em><code>ID</code></em> denotes a D-Bus active connection path in the format of /org/freedesktop/NetworkManager/ActiveConnection/<em><code>num</code></em> or just <em><code>num</code></em>.</td></tr></tbody></table></blockquote><h4 id="show-1">show</h4><p>show 有两种用法，分别是：</p><p>（1）列出活动的连接，或进行排序（<code>+-</code>为升降序）</p><p><code>show [--active] [ --order [+-]category:... ]</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">[root@localhost ~]# nmcli connection show --order +active</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">[root@localhost ~]# nmcli connection show --order -name</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br></pre></td></tr></table></figure><ul><li>NAME：配置文件的名称（可以重复）</li><li>UUID：唯一识别</li><li>TYPE：网络类型</li><li>DEVICE：网卡名称（这里与 <code>nmcli device</code> 保持一致）</li></ul><p>（2）查看指定连接的详细信息</p><p><code>show [--active] [ id | uuid | path | apath ] ID...</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli connection show ens32</span><br><span class="line">connection.id:                          ens32</span><br><span class="line">connection.uuid:                        e1af3066-1f0e-44a5-a4a3-c0174cf77a7b</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h4 id="add">add</h4><p><code>add [save &#123; yes | no &#125;] &#123; option value | [+|-]setting.property value &#125; ...</code></p><p>添加 connection 配置文件（注意网卡只有一个 connection 配置文件是活动的，但是可以存在多个 connection 配置文件，可以通过connection.autoconnect-priority 设置 connection 配置文件的优先级）。</p><p>add 后面必须跟的三个参数：</p><ul><li><p><code>con-name</code>：配置文件的名称</p></li><li><p><code>ifname</code>：网卡的名称</p></li><li><p><code>type</code>：网卡类型一般是 ethernet</p></li></ul><p>add 还可以跟很多参数，比如：</p><ul><li><code>ipv4.address</code></li><li><code>ipv4.gateway</code></li><li><code>ipv4.dns</code></li><li><code>ipv4.method</code>（设置自动连接还是手动连接 auto manual 等等）</li><li><code>connection.autoconnect</code>（开机是否自动连接）</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">[root@localhost ~]# nmcli connection add con-name ens34 ifname ens34 type ethernet ipv4.address 192.168.0.27/24 ipv4.gateway 192.168.0.1 ipv4.dns 114.114.114.114,8.8.8.8 ipv4.method manual connection.autoconnect yes</span><br><span class="line">Connection &#x27;ens34&#x27; (b713041f-bfb5-45ce-a8da-21ad02361e81) successfully added.</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">ens34  b713041f-bfb5-45ce-a8da-21ad02361e81  ethernet  ens34</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE      CONNECTION</span><br><span class="line">ens32   ethernet  connected  ens32</span><br><span class="line">ens33   ethernet  connected  ens33</span><br><span class="line">ens34   ethernet  connected  ens34</span><br><span class="line">lo      loopback  unmanaged  --</span><br></pre></td></tr></table></figure><p>如果在 add 添加 connection 配置文件时没有指定 ipv4.method 则 Linux 会自动获取 IP 地址，即 DHCP。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# ip address show ens34</span><br><span class="line">4: ens34: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000</span><br><span class="line">    link/ether 00:0c:29:66:3f:d9 brd ff:ff:ff:ff:ff:ff</span><br><span class="line">    inet 192.168.0.27/24 brd 192.168.0.255 scope global noprefixroute ens34</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">    inet6 fe80::2b44:f9e6:43f9:6c05/64 scope link noprefixroute</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">[root@localhost ~]# nmcli connection show ens34 | grep &quot;ipv4.&quot;</span><br><span class="line">ipv4.method:                            manual</span><br><span class="line">ipv4.dns:                               114.114.114.114,8.8.8.8</span><br><span class="line">ipv4.dns-search:                        --</span><br><span class="line">ipv4.dns-options:                       --</span><br><span class="line">ipv4.dns-priority:                      0</span><br><span class="line">ipv4.addresses:                         192.168.0.27/24</span><br><span class="line">ipv4.gateway:                           192.168.0.1</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h4 id="delete-1">delete</h4><p><code>delete [ id | uuid | path ] ID...</code></p><p>删除 connection 配置文件。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE      CONNECTION</span><br><span class="line">ens32   ethernet  connected  ens32</span><br><span class="line">ens33   ethernet  connected  ens33</span><br><span class="line">ens34   ethernet  connected  ens34</span><br><span class="line">lo      loopback  unmanaged  --</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">ens34  b713041f-bfb5-45ce-a8da-21ad02361e81  ethernet  ens34</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">[root@localhost ~]<span class="comment"># nmcli connection delete uuid b713041f-bfb5-45ce-a8da-21ad02361e81</span></span></span><br><span class="line">[root@localhost ~]# nmcli connection delete ens34</span><br><span class="line">Connection &#x27;ens34&#x27; (b713041f-bfb5-45ce-a8da-21ad02361e81) successfully deleted.</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br></pre></td></tr></table></figure><h4 id="down">down</h4><p><code>down [ id | uuid | path | apath ] ID...</code></p><p>down 不同于 delete，delete 是将 connection 配置文件删除，而 down 是将 connection 配置文件与网卡的关联断开，与 <code>nmcli device disconnect</code> 效果相同。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  connected     ens33</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">[root@localhost ~]# nmcli connection down ens33</span><br><span class="line">Connection &#x27;ens33&#x27; successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/7)</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  --</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens33   ethernet  disconnected  --</span><br><span class="line">ens34   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br></pre></td></tr></table></figure><h4 id="modify">modify</h4><p><code>modify [--temporary] [ id | uuid | path ] [ID] &#123; option value | [+|-]setting.property value &#125; ...</code></p><p>修改 connection 配置文件信息。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  --</span><br><span class="line">[root@localhost ~]# nmcli connection add con-name ens34 ifname ens34 type ethernet ipv4.address 192.168.0.27/24 ipv4.gateway 192.168.0.1 ipv4.dns 114.114.114.114,8.8.8.8 ipv4.method manual connection.autoconnect yes</span><br><span class="line">Connection &#x27;ens34&#x27; (a0af5eea-3e83-4c13-8104-42857958e050) successfully added.</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens34  a0af5eea-3e83-4c13-8104-42857958e050  ethernet  ens34</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  --</span><br><span class="line">[root@localhost ~]# ip address show ens34</span><br><span class="line">4: ens34: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000</span><br><span class="line">    link/ether 00:0c:29:66:3f:d9 brd ff:ff:ff:ff:ff:ff</span><br><span class="line">    inet 192.168.0.27/24 brd 192.168.0.255 scope global noprefixroute ens34</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">    inet6 fe80::1e7c:8628:7c62:c15b/64 scope link noprefixroute</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">[root@localhost ~]# nmcli connection modify ens34 ifname ens34 ipv4.address 192.168.0.28/24</span><br><span class="line">[root@localhost ~]# ip address show ens34</span><br><span class="line">4: ens34: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000</span><br><span class="line">    link/ether 00:0c:29:66:3f:d9 brd ff:ff:ff:ff:ff:ff</span><br><span class="line">    inet 192.168.0.27/24 brd 192.168.0.255 scope global noprefixroute ens34</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">    inet6 fe80::1e7c:8628:7c62:c15b/64 scope link noprefixroute</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br></pre></td></tr></table></figure><p>这里之所以没有变化是因为我们修改的是硬盘配置文件，加载在内存中 ens34 配置文件的地址还是 192.168.0.27，所以我们需要将修改后的硬盘配置文件 ens34 加载到内存中。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli connection up ens34</span><br><span class="line">Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/12)</span><br><span class="line">[root@localhost ~]# ip address show ens34</span><br><span class="line">4: ens34: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000</span><br><span class="line">    link/ether 00:0c:29:66:3f:d9 brd ff:ff:ff:ff:ff:ff</span><br><span class="line">    inet 192.168.0.28/24 brd 192.168.0.255 scope global noprefixroute ens34</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">    inet6 fe80::7505:8bb3:79c2:b265/64 scope link noprefixroute</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br></pre></td></tr></table></figure><p>有时候 up 无法改变网卡地址，这时候我们需要 reload 重新连接启动，重新加载到内存中。</p><h4 id="up">up</h4><p><code>up [ id | uuid | path ] ID [ifname ifname] [ap BSSID] [passwd-file file]</code></p><p>激活 connection。将网卡与 connection 配置文件关联起来。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE         CONNECTION</span><br><span class="line">ens32   ethernet  connected     ens32</span><br><span class="line">ens34   ethernet  connected     ens34</span><br><span class="line">ens33   ethernet  disconnected  --</span><br><span class="line">lo      loopback  unmanaged     --</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens34  a0af5eea-3e83-4c13-8104-42857958e050  ethernet  ens34</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  --</span><br><span class="line">[root@localhost ~]# nmcli connection up ens33</span><br><span class="line">Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/10)</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">ens34  a0af5eea-3e83-4c13-8104-42857958e050  ethernet  ens34</span><br><span class="line">[root@localhost ~]# nmcli device status</span><br><span class="line">DEVICE  TYPE      STATE      CONNECTION</span><br><span class="line">ens32   ethernet  connected  ens32</span><br><span class="line">ens33   ethernet  connected  ens33</span><br><span class="line">ens34   ethernet  connected  ens34</span><br><span class="line">lo      loopback  unmanaged  --</span><br></pre></td></tr></table></figure><h4 id="clone">clone</h4><p><code>clone [--temporary] [ id | uuid | path ] ID new_name</code></p><p>克隆 connection 配置文件，除了连接名称和 uuid 是新生成的，其他都是一样的。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">ens34  a0af5eea-3e83-4c13-8104-42857958e050  ethernet  ens34</span><br><span class="line">[root@localhost ~]# nmcli connection clone ens34 ens35</span><br><span class="line">ens34 (a0af5eea-3e83-4c13-8104-42857958e050) cloned as ens35 (0ff07cb2-a9a8-4d68-8ad8-46096657cb41).</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  51b34523-8ea7-48f2-84c0-11d3f9aa828f  ethernet  ens33</span><br><span class="line">ens34  a0af5eea-3e83-4c13-8104-42857958e050  ethernet  ens34</span><br><span class="line">ens35  0ff07cb2-a9a8-4d68-8ad8-46096657cb41  ethernet  --</span><br></pre></td></tr></table></figure><h4 id="load">load</h4><p><code>load filename...</code></p><p>从磁盘加载/重新加载一个或多个 connection 配置文件。手动编辑连接文件后使用此命令可确保 NetworkManager 了解其最新状态。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# ls /etc/sysconfig/network-scripts/</span><br><span class="line">ifcfg-ens32  ifcfg-ens33</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">[root@localhost ~]# nmcli connection load /etc/sysconfig/network-scripts/ifcfg-ens33</span><br><span class="line">[root@localhost ~]# nmcli connection show</span><br><span class="line">NAME   UUID                                  TYPE      DEVICE</span><br><span class="line">ens32  e1af3066-1f0e-44a5-a4a3-c0174cf77a7b  ethernet  ens32</span><br><span class="line">ens33  84f8195b-1a23-44a7-b9a2-2a5a25fd777b  ethernet  --</span><br></pre></td></tr></table></figure><h3 id="无线传输控制命令radio-transmission-control-commands">无线传输控制命令（Radio Transmission Control Commands）</h3><p>显示无线开关状态，或启用和禁用开关。</p><p><code>nmcli radio &#123; all | wifi | wwan &#125; [ARGUMENTS...]</code></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# nmcli radio all</span><br><span class="line">WIFI-HW  WIFI     WWAN-HW  WWAN</span><br><span class="line">missing  enabled  missing  enabled</span><br><span class="line">[root@localhost ~]# nmcli radio all off</span><br><span class="line">[root@localhost ~]# nmcli radio all</span><br><span class="line">WIFI-HW  WIFI      WWAN-HW  WWAN</span><br><span class="line">missing  disabled  missing  disabled</span><br><span class="line">[root@localhost ~]# nmcli radio wifi on</span><br><span class="line">[root@localhost ~]# nmcli radio all</span><br><span class="line">WIFI-HW  WIFI     WWAN-HW  WWAN</span><br><span class="line">missing  enabled  missing  disabled</span><br><span class="line">[root@localhost ~]# nmcli radio wwan on</span><br><span class="line">[root@localhost ~]# nmcli radio all</span><br><span class="line">WIFI-HW  WIFI     WWAN-HW  WWAN</span><br><span class="line">missing  enabled  missing  enabled</span><br></pre></td></tr></table></figure><h3 id="活动监视器activity-monitor">活动监视器（Activity Monitor）</h3><p>观察 NetworkManager 活动。 监视连接状态、设备或连接配置文件的变化。</p><p>另请参阅 <strong>nmcli connection monitor</strong> 和 <strong>nmcli device monitor</strong> 以监视某些设备或连接的变化。</p><h3 id="退出状态码">退出状态码</h3><p>nmcli 如果成功退出状态值为 0，如果发生错误则返回大于 0 的值。</p><ul><li><strong>0</strong>: 成功-指示操作已成功</li><li><strong>1</strong>: 位置或指定的错误</li><li><strong>2</strong>: 无效的用户输入，错误的nmcli调用</li><li><strong>3</strong>: 超时了（请参阅 --wait 选项）</li><li><strong>4</strong>: 连接激活失败</li><li><strong>5</strong>: 连接停用失败</li><li><strong>6</strong>: 断开设备失败</li><li><strong>7</strong>: 连接删除失败</li><li><strong>8</strong>: 网络管理器没有运行</li><li><strong>10</strong>: 连接、设备或接入点不存在</li><li><strong>65</strong>: 当使用 <code>--complete-args</code> 选项，文件名应遵循。</li></ul><h2 id="nmcli-示例">nmcli 示例</h2><p>本节介绍 <strong>nmcli</strong> 用法的各种示例。 如果您想要更多信息，请参阅 <a href="https://networkmanager.dev/docs/api/latest/nmcli-examples.html">nmcli-examples(7)</a> 手册页。</p><ul><li><p><code>nmcli -t -f RUNNING general</code></p><p>告诉你 NetworkManager 是否正在运行。</p></li><li><p><code>nmcli -t -f STATE general</code></p><p>显示 NetworkManager 的整体状态。</p></li><li><p><code>nmcli radio wifi off</code></p><p>关闭 Wi-Fi。</p></li><li><p><code>nmcli connection show</code></p><p>列出 NetworkManager 拥有的所有连接。</p></li><li><p><code>nmcli -p -m multiline -f all con show</code></p><p>以多行模式显示所有已配置的连接。</p></li><li><p><code>nmcli connection show --active</code></p><p>列出所有当前活动的连接。</p></li><li><p><code>nmcli -f name,autoconnect c s</code></p><p>显示所有连接配置文件名称及其自动连接属性。</p></li><li><p><code>nmcli -p connection show "My default em1"</code></p><p>显示 "My default em1" 连接配置文件的详细信息。</p></li><li><p><code>nmcli --show-secrets connection show "My Home Wi-Fi"</code></p><p>显示 "My Home Wi-Fi" 连接配置文件的详细信息以及所有密码。 如果没有 <code>--show-secrets</code> 选项，则不会显示 secrets。</p></li><li><p><code>nmcli -f active connection show "My default em1"</code></p><p>显示 "My default em1" 活动连接的详细信息，例如 IP、DHCP 信息等。</p></li><li><p><code>nmcli -f profile con s "My wired connection"</code></p><p>显示名称为 "My wired connection" 的连接配置文件的静态配置详细信息。</p></li><li><p><code>nmcli -p con up "My wired connection" ifname eth0</code></p><p>激活接口 eth0 上名为 "My wired connection" 的连接配置文件。 <code>-p</code> 选项使 nmcli 显示激活进度。</p></li><li><p><code>nmcli con up 6b028a27-6dc9-4411-9886-e9ad1dd43761 ap 00:3A:98:7C:42:D3</code></p><p>将 UUID 6b028a27-6dc9-4411-9886-e9ad1dd43761 的 Wi-Fi 连接连接到 BSSID 00:3A:98:7C:42:D3 的 AP。</p></li><li><p><code>nmcli device status</code></p><p>显示所有设备的状态。</p></li><li><p><code>nmcli dev down em2</code></p><p>断开 em2 接口上的连接并将设备标记为不可自动连接。因此，在设备的 "autoconnect" 设置为 TRUE 或用户手动激活连接之前，设备上不会自动激活任何连接。</p></li><li><p><code>nmcli -f GENERAL,WIFI-PROPERTIES dev show wlan0</code></p><p>显示 wlan0 接口的详细信息；仅显示 GENERAL 和 WIFI-PROPERTIES 部分。</p></li><li><p><code>nmcli -f CONNECTIONS device show wlp3s0</code></p><p>显示 Wi-Fi 接口 wlp3s0 的所有可用连接配置文件。</p></li><li><p><code>nmcli dev wifi</code></p><p>列出 NetworkManager 已知的可用 Wi-Fi 接入点。</p></li><li><p><code>nmcli dev wifi con "Cafe Hotspot 1" password caffeine name "My cafe"</code></p><p>创建一个名为 "My cafe" 的新连接，然后使用密码 "caffeine" 将其连接到 "Cafe Hotspot 1" SSID。这主要在第一次连接到 "Cafe Hotspot 1" 时有用。下次，最好使用 <strong>nmcli con up id "My cafe"</strong>，以便可以使用现有的连接配置文件，而不会创建额外的连接配置文件。</p></li><li><p><code>nmcli -s dev wifi hotspot con-name QuickHotspot</code></p><p>创建热点配置文件并连接它。打印用户从其他设备连接到热点时应使用的热点密码。</p></li><li><p><code>nmcli dev modify em1 ipv4.method shared</code></p><p>使用 em1 设备启动 IPv4 连接共享。共享将一直有效，直到设备断开连接。</p></li><li><p><code>nmcli dev modify em1 ipv6.address 2001:db8::a:bad:c0de</code></p><p>临时向设备添加 IP 地址。当再次激活同一连接时，该地址将被删除。</p></li><li><p><code>nmcli connection add type ethernet autoconnect no ifname eth0</code></p><p>以非交互方式添加与具有自动 IP 配置 (DHCP) 的 eth0 接口绑定的以太网连接，并禁用该连接的 "autoconnect" 标志。</p></li><li><p><code>nmcli c a ifname Maxipes-fik type vlan dev eth0 id 55</code></p><p>以非交互方式添加 ID 为 55 的 VLAN 连接。该连接将使用 eth0，VLAN 接口将命名为 Maxipes-fik。</p></li><li><p><code>nmcli c a ifname eth0 type ethernet ipv4.method disabled ipv6.method link-local</code></p><p>以非交互方式添加将使用 eth0 以太网接口且仅配置了 IPv6 链路本地地址的连接。</p></li><li><p><code>nmcli connection edit ethernet-em1-2</code></p><p>在交互式编辑器中编辑现有的 "ethernet-em1-2" 连接。</p></li><li><p><code>nmcli connection edit type ethernet con-name "yet another Ethernet connection"</code></p><p>在交互式编辑器中添加新的以太网连接。</p></li><li><p><code>nmcli con mod ethernet-2 connection.autoconnect no</code></p><p>修改 "ethernet-2" 连接的 "connection" 设置中的 "autoconnect" 属性。</p></li><li><p><code>nmcli con mod "Home Wi-Fi" wifi.mtu 1350</code></p><p>修改 "Home Wi-Fi" 连接 "wifi" 的设置中的 "mtu" 属性。</p></li><li><p><code>nmcli con mod em1-1 ipv4.method manual ipv4.addr "192.168.1.23/24 192.168.1.1, 10.10.1.5/8, 10.0.0.11"</code></p><p>设置手动寻址和 em1-1 配置文件中的地址。</p></li><li><p><code>nmcli con modify ABC +ipv4.dns 8.8.8.8</code></p><p>将 Google 公共 DNS 服务器附加到 ABC 配置文件中的 DNS 服务器。</p></li><li><p><code>nmcli con modify ABC -ipv4.addresses "192.168.100.25/24 192.168.1.1"</code></p><p>从（静态）配置文件 ABC 中删除指定的 IP 地址。</p></li><li><p><code>nmcli con import type openvpn file ~/Downloads/frootvpn.ovpn</code></p><p>将 OpenVPN 配置导入 NetworkManager。</p></li><li><p><code>nmcli con export corp-vpnc /home/joe/corpvpn.conf</code></p><p>将 NetworkManager VPN 配置文件 corp-vpnc 导出为标准 Cisco (vpnc) 配置。</p></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了 nmcli (Network Manager Command Line Interface) 网络管理工具，它是 NetworkManager 的命令行工具，用于在 Linux 系统上管理网络连接。nmcli 允许用户通过命令行界面管理和配置网络连接，包括无线网络、以太网、VPN 等。本文详细介绍了 nmcli 的安装方法和各种命令，包括选项、通用命令、网络控制命令、设备管理命令、连接管理命令、无线传输控制命令和活动监视器。此外，本文还提供了 nmcli 的示例，供用户参考。nmcli 是一个功能强大且灵活的工具，常用于服务器管理和命令行环境下的网络配置。&lt;/p&gt;</summary>
    
    
    
    <category term="os" scheme="https://www.wylu.me/categories/os/"/>
    
    <category term="centos" scheme="https://www.wylu.me/categories/os/centos/"/>
    
    
    <category term="centos" scheme="https://www.wylu.me/tags/centos/"/>
    
    <category term="nmcli" scheme="https://www.wylu.me/tags/nmcli/"/>
    
  </entry>
  
  <entry>
    <title>kafka 安装部署与安全认证</title>
    <link href="https://www.wylu.me/posts/ef509ce2/"/>
    <id>https://www.wylu.me/posts/ef509ce2/</id>
    <published>2023-07-29T10:22:39.000Z</published>
    <updated>2023-07-29T10:24:12.612Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了如何在 Kafka 中实现身份验证和授权功能，具体使用了 JAAS（Java Authentication and Authorization Service）机制。在安装部署方面，需要先安装 JDK，并准备好一个 Zookeeper 环境。接着，按照下载、解压、编辑配置文件、配置 JAAS、创建服务、启动 Kafka 等步骤进行操作。最后，通过创建、查看、生产、消费、删除 topic 和查看消费群组等测试，验证了集群的正常运行。</p><span id="more"></span><h1 id="kafka-安装部署与安全认证">kafka 安装部署与安全认证</h1><p>Kafka JAAS（Java Authentication and Authorization Service）是一种用于 Apache Kafka 的身份验证和授权机制。JAAS 是 Java 平台的标准身份验证和授权框架，它提供了一种可插拔的方式来实现身份验证和授权功能。</p><p>Kafka JAAS 可以用于保护 Kafka 集群和客户端之间的通信安全。它允许管理员配置不同的身份验证机制，例如用户名/密码、Kerberos 或 SSL/TLS 证书等。通过 JAAS，Kafka 可以验证客户端的身份，并根据配置的授权策略来决定客户端是否有权访问特定的主题或执行特定的操作。</p><p>要使用 Kafka JAAS，您需要在 Kafka 服务器和客户端的配置文件中进行相应的配置。在服务器端，您可以配置 JAAS 模块来指定身份验证和授权的方式。在客户端，您需要提供与服务器端相匹配的 JAAS 配置，并在连接到 Kafka 集群时使用该配置。</p><p>总之，Kafka JAAS 提供了一种灵活且可扩展的方式来实现 Kafka 的身份验证和授权，以增强 Kafka 集群的安全性。</p><h2 id="安装-jdk">1. 安装 jdk</h2><p>如果未安装 java 环境，请先执行以下命令安装 openjdk-1.8.0</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum -y install java-1.8.0-openjdk java-1.8.0-openjdk-devel</span><br></pre></td></tr></table></figure><h2 id="设备环境">2. 设备环境</h2><p>Kafka 集群环境搭建，需要准备好一个 zookeeper 环境（集群）</p><table><thead><tr class="header"><th>Host</th><th>IP</th><th>Port</th><th>OS</th><th>Software</th></tr></thead><tbody><tr class="odd"><td>saas_kafka_01.com</td><td>10.128.170.21</td><td>9092</td><td>CentOS 7.9.2009</td><td>kafka 2.13-2.8.1</td></tr><tr class="even"><td>saas_kafka_02.com</td><td>10.128.170.22</td><td>9092</td><td>CentOS 7.9.2009</td><td>kafka 2.13-2.8.1</td></tr><tr class="odd"><td>saas_kafka_03.com</td><td>10.128.170.23</td><td>9092</td><td>CentOS 7.9.2009</td><td>kafka 2.13-2.8.1</td></tr></tbody></table><p>说明：kafka 名中的 2.13 是 Scala 语言版本，后面的 2.8.1 是 kafka 版本，端口默认为 9092。</p><h2 id="安装步骤">3. 安装步骤</h2><h3 id="下载-kafka">3.1 下载 kafka</h3><p>官网下载太慢，推荐使用国内镜像进行下载，清华镜像下载地址：</p><p><a href="https://mirrors.tuna.tsinghua.edu.cn/apache/kafka" class="uri">https://mirrors.tuna.tsinghua.edu.cn/apache/kafka</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget -c https://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.8.1/kafka_2.13-2.8.1.tgz</span><br></pre></td></tr></table></figure><h3 id="解压安装">3.2 解压安装</h3><p>这里解压至 /virus 目录：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -zxvf kafka_2.13-2.8.1.tgz -C /virus</span><br></pre></td></tr></table></figure><h3 id="编辑配置文件">3.3 编辑配置文件</h3><p>进入 config 目录：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd /virus/kafka_2.13-2.8.1/config/</span><br></pre></td></tr></table></figure><p>备份原配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp server.properties server.properties.bak</span><br></pre></td></tr></table></figure><p>编辑配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim server.properties</span><br></pre></td></tr></table></figure><p>修改配置如下：</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Licensed to the Apache Software Foundation (ASF) under one or more</span></span><br><span class="line"><span class="comment"># contributor license agreements.  See the NOTICE file distributed with</span></span><br><span class="line"><span class="comment"># this work for additional information regarding copyright ownership.</span></span><br><span class="line"><span class="comment"># The ASF licenses this file to You under the Apache License, Version 2.0</span></span><br><span class="line"><span class="comment"># (the &quot;License&quot;); you may not use this file except in compliance with</span></span><br><span class="line"><span class="comment"># the License.  You may obtain a copy of the License at</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#    http://www.apache.org/licenses/LICENSE-2.0</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Unless required by applicable law or agreed to in writing, software</span></span><br><span class="line"><span class="comment"># distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</span></span><br><span class="line"><span class="comment"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span></span><br><span class="line"><span class="comment"># See the License for the specific language governing permissions and</span></span><br><span class="line"><span class="comment"># limitations under the License.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># see kafka.server.KafkaConfig for additional details and defaults</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">############################# Server Basics #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The id of the broker. This must be set to a unique integer for each broker.</span></span><br><span class="line"><span class="attr">broker.id</span>=<span class="string">1</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">############################# Socket Server Settings #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The address the socket server listens on. It will get the value returned from</span></span><br><span class="line"><span class="comment"># java.net.InetAddress.getCanonicalHostName() if not configured.</span></span><br><span class="line"><span class="comment">#   FORMAT:</span></span><br><span class="line"><span class="comment">#     listeners = listener_name://host_name:port</span></span><br><span class="line"><span class="comment">#   EXAMPLE:</span></span><br><span class="line"><span class="comment">#     listeners = PLAINTEXT://your.host.name:9092</span></span><br><span class="line"><span class="comment">#listeners=PLAINTEXT://:9092</span></span><br><span class="line"><span class="attr">listeners</span>=<span class="string">SASL_PLAINTEXT://saas_kafka_01.com:9092</span></span><br><span class="line"><span class="attr">security.inter.broker.protocol</span>=<span class="string">SASL_PLAINTEXT</span></span><br><span class="line"><span class="attr">sasl.enabled.mechanisms</span>=<span class="string">PLAIN</span></span><br><span class="line"><span class="attr">sasl.mechanism.inter.broker.protocol</span>=<span class="string">PLAIN</span></span><br><span class="line"><span class="attr">authorizer.class.name</span>=<span class="string">kafka.security.auth.SimpleAclAuthorizer</span></span><br><span class="line"><span class="attr">super.users</span>=<span class="string">User:admin</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Hostname and port the broker will advertise to producers and consumers. If not set,</span></span><br><span class="line"><span class="comment"># it uses the value for &quot;listeners&quot; if configured.  Otherwise, it will use the value</span></span><br><span class="line"><span class="comment"># returned from java.net.InetAddress.getCanonicalHostName().</span></span><br><span class="line"><span class="comment">#advertised.listeners=PLAINTEXT://your.host.name:9092</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Maps listener names to security protocols, the default is for them to be the same. See the config documentation for more details</span></span><br><span class="line"><span class="comment">#listener.security.protocol.map=PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The number of threads that the server uses for receiving requests from the network and sending responses to the network</span></span><br><span class="line"><span class="attr">num.network.threads</span>=<span class="string">3</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The number of threads that the server uses for processing requests, which may include disk I/O</span></span><br><span class="line"><span class="attr">num.io.threads</span>=<span class="string">8</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The send buffer (SO_SNDBUF) used by the socket server</span></span><br><span class="line"><span class="attr">socket.send.buffer.bytes</span>=<span class="string">102400000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The receive buffer (SO_RCVBUF) used by the socket server</span></span><br><span class="line"><span class="attr">socket.receive.buffer.bytes</span>=<span class="string">102400000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The maximum size of a request that the socket server will accept (protection against OOM)</span></span><br><span class="line"><span class="attr">socket.request.max.bytes</span>=<span class="string">104857600</span></span><br><span class="line"></span><br><span class="line"><span class="attr">message.max.bytes</span>=<span class="string">200000000</span></span><br><span class="line"><span class="attr">message.fetch.max.bytes</span>=<span class="string">200000000</span></span><br><span class="line"><span class="attr">fetch.message.max.bytes</span>=<span class="string">200000000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">############################# Log Basics #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># A comma separated list of directories under which to store log files</span></span><br><span class="line"><span class="attr">log.dirs</span>=<span class="string">/saasdata/kafka/data</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The default number of log partitions per topic. More partitions allow greater</span></span><br><span class="line"><span class="comment"># parallelism for consumption, but this will also result in more files across</span></span><br><span class="line"><span class="comment"># the brokers.</span></span><br><span class="line"><span class="attr">num.partitions</span>=<span class="string">1</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The number of threads per data directory to be used for log recovery at startup and flushing at shutdown.</span></span><br><span class="line"><span class="comment"># This value is recommended to be increased for installations with data dirs located in RAID array.</span></span><br><span class="line"><span class="attr">num.recovery.threads.per.data.dir</span>=<span class="string">1</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">############################# Internal Topic Settings  #############################</span></span><br><span class="line"><span class="comment"># The replication factor for the group metadata internal topics &quot;__consumer_offsets&quot; and &quot;__transaction_state&quot;</span></span><br><span class="line"><span class="comment"># For anything other than development testing, a value greater than 1 is recommended to ensure availability such as 3.</span></span><br><span class="line"><span class="attr">offsets.topic.replication.factor</span>=<span class="string">1</span></span><br><span class="line"><span class="attr">transaction.state.log.replication.factor</span>=<span class="string">1</span></span><br><span class="line"><span class="attr">transaction.state.log.min.isr</span>=<span class="string">1</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">############################# Log Flush Policy #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Messages are immediately written to the filesystem but by default we only fsync() to sync</span></span><br><span class="line"><span class="comment"># the OS cache lazily. The following configurations control the flush of data to disk.</span></span><br><span class="line"><span class="comment"># There are a few important trade-offs here:</span></span><br><span class="line"><span class="comment">#    1. Durability: Unflushed data may be lost if you are not using replication.</span></span><br><span class="line"><span class="comment">#    2. Latency: Very large flush intervals may lead to latency spikes when the flush does occur as there will be a lot of data to flush.</span></span><br><span class="line"><span class="comment">#    3. Throughput: The flush is generally the most expensive operation, and a small flush interval may lead to excessive seeks.</span></span><br><span class="line"><span class="comment"># The settings below allow one to configure the flush policy to flush data after a period of time or</span></span><br><span class="line"><span class="comment"># every N messages (or both). This can be done globally and overridden on a per-topic basis.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The number of messages to accept before forcing a flush of data to disk</span></span><br><span class="line"><span class="comment">#log.flush.interval.messages=10000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The maximum amount of time a message can sit in a log before we force a flush</span></span><br><span class="line"><span class="comment">#log.flush.interval.ms=1000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">############################# Log Retention Policy #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The following configurations control the disposal of log segments. The policy can</span></span><br><span class="line"><span class="comment"># be set to delete segments after a period of time, or after a given size has accumulated.</span></span><br><span class="line"><span class="comment"># A segment will be deleted whenever *either* of these criteria are met. Deletion always happens</span></span><br><span class="line"><span class="comment"># from the end of the log.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The minimum age of a log file to be eligible for deletion due to age</span></span><br><span class="line"><span class="attr">log.retention.hours</span>=<span class="string">168</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># A size-based retention policy for logs. Segments are pruned from the log unless the remaining</span></span><br><span class="line"><span class="comment"># segments drop below log.retention.bytes. Functions independently of log.retention.hours.</span></span><br><span class="line"><span class="comment">#log.retention.bytes=1073741824</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The maximum size of a log segment file. When this size is reached a new log segment will be created.</span></span><br><span class="line"><span class="attr">log.segment.bytes</span>=<span class="string">1073741824</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The interval at which log segments are checked to see if they can be deleted according</span></span><br><span class="line"><span class="comment"># to the retention policies</span></span><br><span class="line"><span class="attr">log.retention.check.interval.ms</span>=<span class="string">300000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">############################# Zookeeper #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Zookeeper connection string (see zookeeper docs for details).</span></span><br><span class="line"><span class="comment"># This is a comma separated host:port pairs, each corresponding to a zk</span></span><br><span class="line"><span class="comment"># server. e.g. &quot;127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002&quot;.</span></span><br><span class="line"><span class="comment"># You can also append an optional chroot string to the urls to specify the</span></span><br><span class="line"><span class="comment"># root directory for all kafka znodes.</span></span><br><span class="line"><span class="attr">zookeeper.connect</span>=<span class="string">saas_kafka_01.com:2181,saas_kafka_02.com:2181,saas_kafka_03.com:2181</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Timeout in ms for connecting to zookeeper</span></span><br><span class="line"><span class="attr">zookeeper.connection.timeout.ms</span>=<span class="string">18000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">############################# Group Coordinator Settings #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The following configuration specifies the time, in milliseconds, that the GroupCoordinator will delay the initial consumer rebalance.</span></span><br><span class="line"><span class="comment"># The rebalance will be further delayed by the value of group.initial.rebalance.delay.ms as new members join the group, up to a maximum of max.poll.interval.ms.</span></span><br><span class="line"><span class="comment"># The default value for this is 3 seconds.</span></span><br><span class="line"><span class="comment"># We override this to 0 here as it makes for a better out-of-the-box experience for development and testing.</span></span><br><span class="line"><span class="comment"># However, in production environments the default value of 3 seconds is more suitable as this will help to avoid unnecessary, and potentially expensive, rebalances during application startup.</span></span><br><span class="line"><span class="attr">group.initial.rebalance.delay.ms</span>=<span class="string">0</span></span><br><span class="line"></span><br><span class="line"><span class="attr">replica.fetch.max.bytes</span>=<span class="string">11240000</span></span><br></pre></td></tr></table></figure><p>在 saas_kafka_02.com 和 saas_kafka_03.com 上进行同样的操作，不再赘述。</p><p><strong>需要注意的是，<code>broker.id</code> 和 <code>listeners</code> 需要根据实际情况配置。</strong></p><ul><li>saas_kafka_01.com<ul><li><code>broker.id=1</code></li><li><code>listeners=SASL_PLAINTEXT://saas_kafka_01.com:9092</code></li></ul></li><li>saas_kafka_02.com<ul><li><code>broker.id=2</code></li><li><code>listeners=SASL_PLAINTEXT://saas_kafka_02.com:9092</code></li></ul></li><li>saas_kafka_03.com<ul><li><code>broker.id=3</code></li><li><code>listeners=SASL_PLAINTEXT://saas_kafka_03.com:9092</code></li></ul></li></ul><h3 id="配置-jaas">3.4 配置 jaas</h3><p>在 config 目录创建 kafka_jaas.conf 文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /virus/kafka_2.13-2.8.1/config/kafka_jaas.conf</span><br></pre></td></tr></table></figure><p>添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">KafkaServer &#123;</span><br><span class="line">    org.apache.kafka.common.security.plain.PlainLoginModule required</span><br><span class="line">    username=&quot;admin&quot;</span><br><span class="line">    password=&quot;saas_kafka_root123&quot;</span><br><span class="line">    user_admin=&quot;saas_kafka_root123&quot;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">KafkaClient &#123;</span><br><span class="line">    org.apache.kafka.common.security.plain.PlainLoginModule required</span><br><span class="line">    username=&quot;admin&quot;</span><br><span class="line">    password=&quot;saas_kafka_root123&quot;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h3 id="创建-system-服务">3.5 创建 system 服务</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /usr/lib/systemd/system/kafka.service</span><br></pre></td></tr></table></figure><p>添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=kafka service</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=forking</span><br><span class="line">User=root</span><br><span class="line">Group=root</span><br><span class="line">Environment=&quot;JMX_PORT=9999&quot;</span><br><span class="line">Environment=&quot;KAFKA_OPTS=-Djava.security.auth.login.config=/virus/kafka_2.13-2.8.1/config/kafka_jaas.conf&quot;</span><br><span class="line">ExecStart=/virus/kafka_2.13-2.8.1/bin/kafka-server-start.sh -daemon /virus/kafka_2.13-2.8.1/config/server.properties</span><br><span class="line">ExecStop= /virus/kafka_2.13-2.8.1/bin/kafka-server-stop.sh</span><br><span class="line">Restart=always</span><br><span class="line">RestartSec=1</span><br><span class="line">StartLimitIntervalSec=0</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure><h3 id="启动-kafka">3.6 启动 kafka</h3><p>启动节点 saas_kafka_01.com，同样地启动节点 saas_kafka_02.com，saas_kafka_03.com</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl start kafka.service</span><br></pre></td></tr></table></figure><p>查看 9092 端口状态，确保服务已经启动：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netstat -anptl | grep 9092</span><br></pre></td></tr></table></figure><h3 id="查看集群状态">3.7 查看集群状态</h3><p>登录 zookeeper（切换到 zookeeper 的 bin 目录下）：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@saas_kafka_01.com bin]# cd /virus/apache-zookeeper-3.6.3-bin/bin/</span><br><span class="line">[root@saas_kafka_01.com bin]# ./zkCli.sh -server saas_kafka_02.com</span><br><span class="line">...</span><br><span class="line">[zk: saas_kafka_02.com(CONNECTED) 0] ls /</span><br><span class="line">[admin, brokers, cluster, config, consumers, controller, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification, zookeeper]</span><br><span class="line">[zk: saas_kafka_02.com(CONNECTED) 1] ls /brokers</span><br><span class="line">[ids, seqid, topics]</span><br><span class="line">[zk: saas_kafka_02.com(CONNECTED) 2] ls /brokers/ids</span><br><span class="line">[1, 2, 3]</span><br></pre></td></tr></table></figure><h2 id="集群测试">4. 集群测试</h2><h3 id="创建-topic">4.1 创建 topic</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/virus/kafka_2.13-2.8.1/bin/kafka-topics.sh --create --zookeeper saas_kafka_01.com:2181 --replication-factor 3 --partitions 3 --topic google</span><br></pre></td></tr></table></figure><h3 id="查看-topic">4.2 查看 topic</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/virus/kafka_2.13-2.8.1/bin/kafka-topics.sh --describe google --zookeeper saas_kafka_01.com:2181</span><br></pre></td></tr></table></figure><h3 id="生产消息">4.3 生产消息</h3><p>在 /virus/kafka_2.13-2.8.1/config/producer.properties 添加如下配置：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">security.protocol</span>=SASL_PLAINTEXT</span><br><span class="line"><span class="attr">sasl.mechanism</span>=PLAIN</span><br></pre></td></tr></table></figure><p>生产消息：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# export KAFKA_OPTS=&#x27;-Djava.security.auth.login.config=/virus/kafka_2.13-2.8.1/config/kafka_jaas.conf&#x27;</span><br><span class="line">[root@localhost ~]# /virus/kafka_2.13-2.8.1/bin/kafka-console-producer.sh --broker-list saas_kafka_01.com:9092 --topic google --producer.config /virus/kafka_2.13-2.8.1/config/producer.properties</span><br><span class="line"><span class="meta prompt_">&gt;</span><span class="language-bash">Apple</span></span><br><span class="line"><span class="meta prompt_">&gt;</span><span class="language-bash">Banana</span></span><br><span class="line"><span class="meta prompt_">&gt;</span><span class="language-bash">Cat</span></span><br><span class="line"><span class="meta prompt_">&gt;</span><span class="language-bash">Dog</span></span><br></pre></td></tr></table></figure><h3 id="消费消息">4.4 消费消息</h3><p>在 /virus/kafka_2.13-2.8.1/config/consumer.properties 添加如下配置：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">security.protocol</span>=SASL_PLAINTEXT</span><br><span class="line"><span class="attr">sasl.mechanism</span>=PLAIN</span><br></pre></td></tr></table></figure><p>消费消息：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# export KAFKA_OPTS=&#x27;-Djava.security.auth.login.config=/virus/kafka_2.13-2.8.1/config/kafka_jaas.conf&#x27;</span><br><span class="line">[root@localhost ~]# /virus/kafka_2.13-2.8.1/bin/kafka-console-consumer.sh --bootstrap-server saas_kafka_02.com:9092 --topic google --from-beginning --consumer.config /virus/kafka_2.13-2.8.1/config/consumer.properties</span><br><span class="line">Banana</span><br><span class="line">Apple</span><br><span class="line">Dog</span><br><span class="line">Cat</span><br></pre></td></tr></table></figure><h3 id="删除-topic">4.5 删除 topic</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# /virus/kafka_2.13-2.8.1/bin/kafka-topics.sh --list --zookeeper saas_kafka_01.com:2181</span><br><span class="line">__consumer_offsets</span><br><span class="line">google</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# /virus/kafka_2.13-2.8.1/bin/kafka-topics.sh --delete --zookeeper saas_kafka_01.com:2181 --topic google</span><br></pre></td></tr></table></figure><h3 id="查看消费群组">4.6 查看消费群组</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# export KAFKA_OPTS=&#x27;-Djava.security.auth.login.config=/virus/kafka_2.13-2.8.1/config/kafka_jaas.conf&#x27;</span><br><span class="line">[root@localhost ~]# /virus/kafka_2.13-2.8.1/bin/kafka-consumer-groups.sh --bootstrap-server saas_kafka_01.com:9092 --list --command-config /virus/kafka_2.13-2.8.1/config/consumer.properties</span><br><span class="line">[2022-03-04 01:32:26,611] WARN The configuration &#x27;group.id&#x27; was supplied but isn&#x27;t a known config. (org.apache.kafka.clients.admin.AdminClientConfig)</span><br><span class="line">saasdc_asset_neo4j_group</span><br><span class="line">efak.system.group</span><br><span class="line">saasdc_asset_mongo_group</span><br></pre></td></tr></table></figure><p>查看消费组详情：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# /virus/kafka_2.13-2.8.1/bin/kafka-consumer-groups.sh --bootstrap-server saas_kafka_01.com:9092 --command-config /virus/kafka_2.13-2.8.1/config/consumer.properties --describe --group saasdc_asset_neo4j_group</span><br><span class="line">[2022-03-04 01:45:41,039] WARN The configuration &#x27;group.id&#x27; was supplied but isn&#x27;t a known config. (org.apache.kafka.clients.admin.AdminClientConfig)</span><br><span class="line"></span><br><span class="line">GROUP                    TOPIC              PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             CONSUMER-ID                                  HOST            CLIENT-ID</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 1          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 8          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 19         -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 7          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 14         -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 17         1               1               0               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 5          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 12         -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 0          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 2          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 4          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 15         -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 16         1               1               0               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 11         -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 9          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 18         -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 13         -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 6          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 3          -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br><span class="line">saasdc_asset_neo4j_group saasdc_asset_topic 10         -               0               -               rdkafka-daeaa1cb-e784-42e2-85de-c4a8bece0fd2 /10.128.170.28  rdkafka</span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://www.jianshu.com/p/71ef5068b01a">kafka安装配置SASL_PLAINTEXT</a></p><p><a href="https://blog.csdn.net/weixin_34208283/article/details/86000515">kafka集群中jmx端口设置</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了如何在 Kafka 中实现身份验证和授权功能，具体使用了 JAAS（Java Authentication and Authorization Service）机制。在安装部署方面，需要先安装 JDK，并准备好一个 Zookeeper 环境。接着，按照下载、解压、编辑配置文件、配置 JAAS、创建服务、启动 Kafka 等步骤进行操作。最后，通过创建、查看、生产、消费、删除 topic 和查看消费群组等测试，验证了集群的正常运行。&lt;/p&gt;</summary>
    
    
    
    <category term="middleware" scheme="https://www.wylu.me/categories/middleware/"/>
    
    <category term="mq" scheme="https://www.wylu.me/categories/middleware/mq/"/>
    
    <category term="Kafka" scheme="https://www.wylu.me/categories/middleware/mq/Kafka/"/>
    
    
    <category term="kafka" scheme="https://www.wylu.me/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>CentOS7 下 kafka 集群安装部署</title>
    <link href="https://www.wylu.me/posts/3e8270c5/"/>
    <id>https://www.wylu.me/posts/3e8270c5/</id>
    <published>2023-07-27T15:25:52.000Z</published>
    <updated>2023-07-27T15:46:15.666Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了在 CentOS7 下安装和部署 Kafka 集群的步骤。首先介绍了 Kafka 的简介，包括其作为高吞吐量的分布式发布订阅消息系统的特点。然后说明了搭建 Kafka 集群环境所需的设备环境，包括准备一个 Zookeeper 环境。接下来详细介绍了安装步骤，包括下载 Kafka、解压安装、配置环境变量、编辑配置文件、创建 system 服务、启动和关闭 Kafka 以及查看集群状态。最后，介绍了 Kafka 命令行工具的使用，包括测试流程和测试步骤，涵盖了创建 topic、生产消息、消费消息等操作。</p><span id="more"></span><h1 id="centos7-下-kafka-集群安装部署">CentOS7 下 kafka 集群安装部署</h1><h2 id="kafka-简介">1. kafka 简介</h2><p>Apache kafka 是由 Apache 软件基金会开发的一个开源流处理平台，由 Scala 和 Java 编写。Kafka 是一种高吞吐量的分布式发布订阅消息系统，是消息中间件的一种，用于构建实时数据管道和流应用程序，非常流行。</p><p>Kafka官网：<a href="http://kafka.apache.org" class="uri">http://kafka.apache.org</a></p><p>学习推荐：<a href="http://orchome.com/kafka/index" class="uri">http://orchome.com/kafka/index</a></p><p>官网下载：<a href="http://kafka.apache.org/downloads" class="uri">http://kafka.apache.org/downloads</a></p><h2 id="设备环境">2. 设备环境</h2><p>Kafka 集群环境搭建，需要准备好一个 zookeeper 环境（集群）</p><table><thead><tr class="header"><th>Host</th><th>IP</th><th>Port</th><th>OS</th><th>Software</th></tr></thead><tbody><tr class="odd"><td>cnode1</td><td>10.128.170.21</td><td>9092</td><td>CentOS 7.9.2009</td><td>kafka 2.13-2.8.1</td></tr><tr class="even"><td>cnode2</td><td>10.128.170.22</td><td>9092</td><td>CentOS 7.9.2009</td><td>kafka 2.13-2.8.1</td></tr><tr class="odd"><td>cnode3</td><td>10.128.170.23</td><td>9092</td><td>CentOS 7.9.2009</td><td>kafka 2.13-2.8.1</td></tr></tbody></table><p>说明：kafka 名中的 2.13 是 Scala 语言版本，后面的 2.8.1 是 kafka 版本，端口默认为 9092。</p><h2 id="安装步骤">3. 安装步骤</h2><h3 id="下载-kafka">3.1 下载 kafka</h3><p>官网下载太慢，推荐使用国内镜像进行下载，清华镜像下载地址：</p><p><a href="https://mirrors.tuna.tsinghua.edu.cn/apache/kafka" class="uri">https://mirrors.tuna.tsinghua.edu.cn/apache/kafka</a></p><p>这里以下载 2.8.1 版本为例：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget -c https://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.8.1/kafka_2.13-2.8.1.tgz</span><br></pre></td></tr></table></figure><h3 id="解压安装">3.2 解压安装</h3><p>这里解压至 /opt 目录：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -zxvf kafka_2.13-2.8.1.tgz -C /opt</span><br></pre></td></tr></table></figure><p>解压后目录：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 kafka_2.13-2.8.1]# pwd</span><br><span class="line">/opt/kafka_2.13-2.8.1</span><br><span class="line">[root@cnode1 kafka_2.13-2.8.1]# ll</span><br><span class="line">total 40</span><br><span class="line">drwxr-xr-x. 3 root root  4096 Sep 14 21:09 bin</span><br><span class="line">drwxr-xr-x. 3 root root  4096 Sep 14 21:09 config</span><br><span class="line">drwxr-xr-x. 2 root root  8192 Feb 18 10:26 libs</span><br><span class="line">-rw-r--r--. 1 root root 14520 Sep 14 21:03 LICENSE</span><br><span class="line">drwxr-xr-x. 2 root root   262 Sep 14 21:09 licenses</span><br><span class="line">-rw-r--r--. 1 root root   953 Sep 14 21:03 NOTICE</span><br><span class="line">drwxr-xr-x. 2 root root    44 Sep 14 21:09 site-docs</span><br></pre></td></tr></table></figure><h3 id="配置环境变量可选">3.3 配置环境变量（可选）</h3><p>编辑 /etc/profile 文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/profile</span><br></pre></td></tr></table></figure><p>在文件末尾添加如下配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">Kafka Environment</span></span><br><span class="line">export KAFKA_HOME=/opt/kafka_2.13-2.8.1</span><br><span class="line"></span><br><span class="line">export PATH=$&#123;PATH&#125;:$&#123;KAFKA_HOME&#125;/bin</span><br></pre></td></tr></table></figure><p>使配置生效：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source /etc/profile</span><br></pre></td></tr></table></figure><h3 id="编辑配置文件">3.4 编辑配置文件</h3><p>进入 config 目录：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd /opt/kafka_2.13-2.8.1/config/</span><br></pre></td></tr></table></figure><p>备份原配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp server.properties server.properties.bak</span><br></pre></td></tr></table></figure><p>编辑配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim server.properties</span><br></pre></td></tr></table></figure><p>修改配置如下：</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">############################# Server Basics #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The id of the broker. This must be set to a unique integer for each broker.</span></span><br><span class="line"><span class="attr">broker.id</span>=<span class="string">1  # 默认是 0，这里自定义 cnode1:1 cnode2:2 cnode3:3</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Switch to enable topic deletion or not, default value is false</span></span><br><span class="line"><span class="comment">#delete.topic.enable  # 这里为新增行，默认是 false，为 false 时，删除 topic 时不会立即被删掉</span></span><br></pre></td></tr></table></figure><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">############################# Socket Server Settings #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The address the socket server listens on. It will get the value returned from</span></span><br><span class="line"><span class="comment"># java.net.InetAddress.getCanonicalHostName() if not configured.</span></span><br><span class="line"><span class="comment">#   FORMAT:</span></span><br><span class="line"><span class="comment">#     listeners = listener_name://host_name:port</span></span><br><span class="line"><span class="comment">#   EXAMPLE:</span></span><br><span class="line"><span class="comment">#     listeners = PLAINTEXT://your.host.name:9092</span></span><br><span class="line"><span class="comment">#listeners=PLAINTEXT://:9092  # 根据需要修改端口</span></span><br><span class="line"><span class="attr">host.name</span>=<span class="string">cnode1  # 这里为新增行</span></span><br></pre></td></tr></table></figure><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">############################# Log Basics #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># A comma separated list of directories under which to store log files</span></span><br><span class="line"><span class="attr">log.dirs</span>=<span class="string">/data/kafka-logs  # 默认是 /tmp/kafka-logs，这里重新定义了路径</span></span><br></pre></td></tr></table></figure><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">############################# Zookeeper #############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Zookeeper connection string (see zookeeper docs for details).</span></span><br><span class="line"><span class="comment"># This is a comma separated host:port pairs, each corresponding to a zk</span></span><br><span class="line"><span class="comment"># server. e.g. &quot;127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002&quot;.</span></span><br><span class="line"><span class="comment"># You can also append an optional chroot string to the urls to specify the</span></span><br><span class="line"><span class="comment"># root directory for all kafka znodes.</span></span><br><span class="line"><span class="comment"># 这里写上已准备好的 zookeeper 集群环境</span></span><br><span class="line"><span class="attr">zookeeper.connect</span>=<span class="string">cnode1:2181,cnode2:2181,cnode3:2181</span></span><br></pre></td></tr></table></figure><p>其它配置可以保持默认，保存退出。在 cnode2 和 cnode3 上进行同样的操作，不再赘述。</p><h3 id="创建-system-服务可选">3.5 创建 system 服务（可选）</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /usr/lib/systemd/system/kafka.service</span><br></pre></td></tr></table></figure><p>添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=kafka service</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=forking</span><br><span class="line">User=root</span><br><span class="line">Group=root</span><br><span class="line"># 启用 jmx</span><br><span class="line">#Environment=JMX_PORT=9988</span><br><span class="line">ExecStart=/opt/kafka_2.13-2.8.1/bin/kafka-server-start.sh -daemon /opt/kafka_2.13-2.8.1/config/server.properties</span><br><span class="line">ExecStop=/opt/kafka_2.13-2.8.1/bin/kafka-server-stop.sh</span><br><span class="line">Restart=always</span><br><span class="line">RestartSec=1</span><br><span class="line">StartLimitIntervalSec=0</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure><p><a href="https://lists.freedesktop.org/archives/systemd-devel/2017-July/039255.html" class="uri">https://lists.freedesktop.org/archives/systemd-devel/2017-July/039255.html</a></p><h3 id="启动-kafka">3.6 启动 kafka</h3><p>这里以未配置环境变量为例。</p><p>切换到 bin 目录下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd /opt/kafka_2.13-2.8.1/bin/</span><br></pre></td></tr></table></figure><p>启动服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 bin]# ./kafka-server-start.sh -daemon ../config/server.properties</span><br></pre></td></tr></table></figure><p>查看 9092 端口状态，确保服务已经启动：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 bin]# netstat -anptl | grep 9092</span><br><span class="line">tcp6       0      0 10.128.170.21:9092      :::*                    LISTEN      29553/java</span><br><span class="line">tcp6       0      0 10.128.170.21:40092     10.128.170.21:9092      ESTABLISHED 29553/java</span><br><span class="line">tcp6       0      0 10.128.170.21:9092      10.128.170.21:40092     ESTABLISHED 29553/java</span><br></pre></td></tr></table></figure><p>同样地，启动 cnode2 和 cnode3：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode2 bin]# ./kafka-server-start.sh -daemon ../config/server.properties</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode3 bin]# ./kafka-server-start.sh -daemon ../config/server.properties</span><br></pre></td></tr></table></figure><p>至此，kafka 安装启动完成。</p><h3 id="关闭-kafka">3.7 关闭 kafka</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 bin]# ./kafka-server-stop.sh</span><br><span class="line">[root@cnode2 bin]# ./kafka-server-stop.sh</span><br><span class="line">[root@cnode3 bin]# ./kafka-server-stop.sh</span><br></pre></td></tr></table></figure><h3 id="查看集群状态">3.8 查看集群状态</h3><p>登录 zookeeper（切换到 zookeeper 的 bin 目录下）：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 bin]# cd /opt/apache-zookeeper-3.6.3-bin/bin/</span><br><span class="line">[root@cnode1 bin]# ./zkCli.sh -server cnode2</span><br><span class="line">...</span><br><span class="line">[zk: cnode2(CONNECTED) 0] ls /</span><br><span class="line">[admin, brokers, cluster, config, consumers, controller, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification, zookeeper]</span><br><span class="line">[zk: cnode2(CONNECTED) 1] ls /brokers</span><br><span class="line">[ids, seqid, topics]</span><br><span class="line">[zk: cnode2(CONNECTED) 2] ls /brokers/ids</span><br><span class="line">[1, 2, 3]</span><br></pre></td></tr></table></figure><p>说明：zookeeper 集群建好之后，通过 <code>ls /</code> 出来的只有 zookeeper，连接 kafka 使用后，<code>/</code> 下面多了不少东西，其中通过查看 <code>/brokers/ids</code> 可以发现已经检查到了已经安装的三台 kafka 的 broker.id [1,2,3]。</p><h2 id="kafka-命令行工具使用">4. kafka 命令行工具使用</h2><h3 id="测试流程">4.1 测试流程</h3><p>主线：创建 topic，生产消息，消费消息。</p><p>流程命令会连接不同的节点，可以顺带检测集群消息的同步情况。</p><p>（1）在 cnode1 上创建 topic</p><p>（2）在 cnode3 查看 topic</p><p>（3）在 cnode1 上生成消息</p><p>（4）在 cnode2 上消费消息</p><h3 id="测试步骤">4.2 测试步骤</h3><p>（1）先创建一个 topic</p><p>在 cnode1 节点执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 bin]# ./kafka-topics.sh --create --zookeeper cnode1:2181 --replication-factor 3 --partitions 2 --topic google</span><br><span class="line">Created topic google.</span><br></pre></td></tr></table></figure><p>创建一个名叫 "google" 的 topic 成功。</p><p>（2）查看一下建好的 topic</p><p>在 cnode3 节点执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode3 bin]# ./kafka-topics.sh --list --zookeeper cnode3:2181</span><br><span class="line">google</span><br></pre></td></tr></table></figure><p>（3）在建好的 topic 下生产消息</p><p>在 cnode1 节点执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 bin]# ./kafka-console-producer.sh --broker-list cnode1:9092 --topic google</span><br><span class="line"><span class="meta prompt_">&gt;</span><span class="language-bash">Apple</span></span><br><span class="line"><span class="meta prompt_">&gt;</span><span class="language-bash">Banana</span></span><br><span class="line"><span class="meta prompt_">&gt;</span><span class="language-bash">Cat</span></span><br><span class="line"><span class="meta prompt_">&gt;</span><span class="language-bash">Dog</span></span><br></pre></td></tr></table></figure><p>这里生产了 4 个消息，每行一个。</p><p>（4）消费指定 topic 下的消息</p><p>在 cnode2 节点执行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode2 bin]# ./kafka-console-consumer.sh --bootstrap-server cnode2:9092 --topic google --from-beginning</span><br><span class="line">Banana</span><br><span class="line">Dog</span><br><span class="line">Apple</span><br><span class="line">Cat</span><br></pre></td></tr></table></figure><p>（5）删除指定 topic</p><p>先 list 一下然后删除</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@cnode1 bin]# ./kafka-topics.sh --list --zookeeper cnode1:2181</span><br><span class="line">__consumer_offsets</span><br><span class="line">google</span><br><span class="line">[root@cnode1 bin]# ./kafka-topics.sh --delete --zookeeper cnode3:2181 --topic google</span><br><span class="line">Topic google is marked for deletion.</span><br><span class="line">Note: This will have no impact if delete.topic.enable is not set to true.</span><br><span class="line">[root@cnode1 bin]# ./kafka-topics.sh --list --zookeeper cnode1:2181</span><br><span class="line">__consumer_offsets</span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://www.cnblogs.com/ding2016/p/8282907.html">centos7下kafka集群安装部署</a></p><p><a href="https://www.cnblogs.com/ding2016/p/8283796.html">kafka命令行脚本使用</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了在 CentOS7 下安装和部署 Kafka 集群的步骤。首先介绍了 Kafka 的简介，包括其作为高吞吐量的分布式发布订阅消息系统的特点。然后说明了搭建 Kafka 集群环境所需的设备环境，包括准备一个 Zookeeper 环境。接下来详细介绍了安装步骤，包括下载 Kafka、解压安装、配置环境变量、编辑配置文件、创建 system 服务、启动和关闭 Kafka 以及查看集群状态。最后，介绍了 Kafka 命令行工具的使用，包括测试流程和测试步骤，涵盖了创建 topic、生产消息、消费消息等操作。&lt;/p&gt;</summary>
    
    
    
    <category term="middleware" scheme="https://www.wylu.me/categories/middleware/"/>
    
    <category term="mq" scheme="https://www.wylu.me/categories/middleware/mq/"/>
    
    <category term="Kafka" scheme="https://www.wylu.me/categories/middleware/mq/Kafka/"/>
    
    
    <category term="kafka" scheme="https://www.wylu.me/tags/kafka/"/>
    
  </entry>
  
  <entry>
    <title>CentOS7 下安装 RabbitMQ</title>
    <link href="https://www.wylu.me/posts/57eda046/"/>
    <id>https://www.wylu.me/posts/57eda046/</id>
    <published>2023-07-26T15:13:23.000Z</published>
    <updated>2023-07-26T15:14:40.305Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了在 CentOS7 系统下安装 RabbitMQ 的详细步骤。首先需要安装 Erlang 和 RabbitMQ，然后进行 RabbitMQ 的配置，包括设置防火墙和 SELinux。接着介绍了如何启用 RabbitMQ Web 控制台以及如何配置 SSL 证书。最后，文章提供了设置 RabbitMQ 集群的脚本 /usr/local/sbin/rabbitmq-cluster.sh，供读者参考。</p><span id="more"></span><h1 id="centos7-下安装-rabbitmq">CentOS7 下安装 RabbitMQ</h1><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo yum -y install epel-release</span><br><span class="line">sudo yum -y update</span><br></pre></td></tr></table></figure><h2 id="安装-erlang">安装 Erlang</h2><p>下载 erlang repository：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget http://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm</span><br></pre></td></tr></table></figure><p>添加 erlang repository：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rpm -Uvh erlang-solutions-1.0-1.noarch.rpm</span><br></pre></td></tr></table></figure><p>安装 erlang 及其依赖：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo yum -y install erlang socat logrotate</span><br></pre></td></tr></table></figure><h2 id="安装-rabbitmq">安装 RabbitMQ</h2><p>下载 RabbitMQ rpm 包：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.8/rabbitmq-server-3.8.8-1.el6.noarch.rpm</span><br></pre></td></tr></table></figure><p>添加 signing key：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rpm --import https://www.rabbitmq.com/rabbitmq-signing-key-public.asc</span><br></pre></td></tr></table></figure><p>安装 rabbitmq-server：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rpm -Uvh rabbitmq-server-3.8.8-1.el6.noarch.rpm</span><br></pre></td></tr></table></figure><p>启动 RabbitMQ：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl start rabbitmq-server</span><br></pre></td></tr></table></figure><p>设置 RabbitMQ 开机自启：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl enable rabbitmq-server</span><br></pre></td></tr></table></figure><h2 id="rabbitmq-配置可选">RabbitMQ 配置（可选）</h2><p>创建 rabbitmq 配置文件 <code>/etc/rabbitmq/rabbitmq.conf</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">listeners.ssl.default = 5671</span><br><span class="line"></span><br><span class="line">ssl_options.cacertfile = /path/to/cacertfile.pem</span><br><span class="line">ssl_options.certfile   = /path/to/certfile.pem</span><br><span class="line">ssl_options.keyfile    = /path/to/keyfile.pem</span><br><span class="line">ssl_options.verify     = verify_peer</span><br><span class="line">ssl_options.versions.1 = tlsv1.2</span><br><span class="line">ssl_options.versions.2 = tlsv1.1</span><br><span class="line">ssl_options.fail_if_no_peer_cert = false</span><br><span class="line"></span><br><span class="line">tcp_listen_options.backlog       = 128</span><br><span class="line">tcp_listen_options.nodelay       = true</span><br><span class="line">tcp_listen_options.exit_on_close = false</span><br><span class="line">tcp_listen_options.keepalive     = false</span><br><span class="line"></span><br><span class="line">heartbeat = 580</span><br></pre></td></tr></table></figure><h2 id="设置防火墙">设置防火墙</h2><p>设置防火墙规则，放通相关端口：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">sudo firewall-cmd --zone=public --permanent --add-port=4369/tcp</span><br><span class="line">sudo firewall-cmd --zone=public --permanent --add-port=25672/tcp</span><br><span class="line">sudo firewall-cmd --zone=public --permanent --add-port=5671-5672/tcp</span><br><span class="line">sudo firewall-cmd --zone=public --permanent --add-port=15672/tcp</span><br><span class="line">sudo firewall-cmd --zone=public --permanent --add-port=61613-61614/tcp</span><br><span class="line">sudo firewall-cmd --zone=public --permanent --add-port=1883/tcp</span><br><span class="line">sudo firewall-cmd --zone=public --permanent --add-port=8883/tcp</span><br></pre></td></tr></table></figure><p>重载防火墙，使规则生效：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo firewall-cmd --reload</span><br></pre></td></tr></table></figure><h2 id="selinux">SELinux</h2><p>如果 SELinux 是 enabled，则启用 NIS：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo setsebool -P nis_enabled 1</span><br></pre></td></tr></table></figure><p><code>-P</code> 参数表示将设置永久生效，nis_enabled 是一个 SELinux 布尔类型的变量，用于控制是否启用 NIS（Network Information Service）服务。将其设置为 1 表示启用 NIS 服务。</p><h2 id="rabbitmq-web-控制台">RabbitMQ Web 控制台</h2><p>启用 RabbitMQ web 控制台：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rabbitmq-plugins enable rabbitmq_management</span><br></pre></td></tr></table></figure><p>修改文件权限：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chown -R rabbitmq:rabbitmq /var/lib/rabbitmq/</span><br></pre></td></tr></table></figure><p>创建一个 admin 用户（将 <code>password</code> 替换为一个强密码）：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rabbitmqctl add_user admin password</span><br></pre></td></tr></table></figure><p>给 admin 用户设置 administrator 标签：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rabbitmqctl set_user_tags admin administrator</span><br></pre></td></tr></table></figure><p>设置 admin 用户权限：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo rabbitmqctl set_permissions -p / admin &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;</span><br></pre></td></tr></table></figure><p>使用 admin 用户访问 RabbitMQ web 控制台：</p><p><a href="http://Your_Server_IP:15672" class="uri">http://Your_Server_IP:15672</a></p><h2 id="rabbitmq-web-控制台配置-ssl-证书可选">RabbitMQ Web 控制台配置 SSL 证书（可选）</h2><p>编辑 RabbitMQ 配置文件 <code>/etc/rabbitmq/rabbitmq.conf</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">management.listener.port = 15672</span><br><span class="line">management.listener.ssl  = true</span><br><span class="line"></span><br><span class="line">management.listener.ssl_opts.cacertfile = /path/to/cacertfile.pem</span><br><span class="line">management.listener.ssl_opts.certfile   = /path/to/certfile.pem</span><br><span class="line">management.listener.ssl_opts.keyfile    = /path/to/keyfile.pem</span><br></pre></td></tr></table></figure><h2 id="rabbitmq-集群">RabbitMQ 集群</h2><p>设置 RabbitMQ 集群, 拷贝下面的脚本 <code>/usr/local/sbin/rabbitmq-cluster.sh</code> 并运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="function"><span class="title">getHostname</span></span>()</span><br><span class="line">&#123;</span><br><span class="line">  <span class="built_in">local</span> HOST=<span class="string">&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">while</span> <span class="built_in">test</span> -z <span class="string">&quot;<span class="variable">$HOST</span>&quot;</span></span><br><span class="line">  <span class="keyword">do</span></span><br><span class="line">    <span class="built_in">read</span> -p <span class="string">&quot;<span class="variable">$1</span> : &quot;</span> HOST</span><br><span class="line">  <span class="keyword">done</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">echo</span> <span class="variable">$HOST</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">SETUP_MASTER_SCRIPT=<span class="string">&#x27;</span></span><br><span class="line"><span class="string">rabbitmqctl stop_app;</span></span><br><span class="line"><span class="string">rabbitmqctl reset;</span></span><br><span class="line"><span class="string">rabbitmqctl start_app;</span></span><br><span class="line"><span class="string">&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Step 1 : Setup the Master. Get the erlang cookie</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Setup RabbitMQ Master&quot;</span>;</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;=====================&quot;</span>;</span><br><span class="line"></span><br><span class="line">OUT=/tmp/master.out</span><br><span class="line">MASTER_HOSTNAME=$(getHostname <span class="string">&quot;Enter the master server&#x27;s hostname&quot;</span>);</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="variable">$MASTER_HOSTNAME</span>] Setting up master&quot;</span>;</span><br><span class="line">ssh -t <span class="variable">$MASTER_HOSTNAME</span> <span class="string">&quot;bash -c &#x27;<span class="variable">$SETUP_MASTER_SCRIPT</span> cat /var/lib/rabbitmq/.erlang.cookie;&#x27;&quot;</span> | <span class="built_in">tee</span> <span class="variable">$OUT</span>;</span><br><span class="line">COOKIE=$(<span class="built_in">cat</span> <span class="variable">$OUT</span> | <span class="built_in">tail</span> -n1)</span><br><span class="line"><span class="built_in">rm</span> <span class="variable">$OUT</span>;</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Master&#x27;s Erlang Cookie : &#x27;<span class="variable">$COOKIE</span>&#x27;&quot;</span></span><br><span class="line"></span><br><span class="line">MASTER_IP=$(getHostname <span class="string">&quot;Enter the master server&#x27;s IP as seen from the slaves (Use a local IP if available)&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Step 2 : Setup the slaves</span></span><br><span class="line"></span><br><span class="line">SETUP_SLAVE_SCRIPT=<span class="string">&quot;</span></span><br><span class="line"><span class="string">sed -i \&quot;s/^$/<span class="variable">$MASTER_IP</span>    <span class="variable">$MASTER_HOSTNAME</span>\n/\&quot; /etc/hosts</span></span><br><span class="line"><span class="string">bash -c \&quot;echo -n &#x27;<span class="variable">$COOKIE</span>&#x27; &gt; /var/lib/rabbitmq/.erlang.cookie\&quot;;</span></span><br><span class="line"><span class="string">rabbitmqctl stop_app;</span></span><br><span class="line"><span class="string">rabbitmqctl reset;</span></span><br><span class="line"><span class="string">rabbitmqctl join_cluster --ram rabbit@<span class="variable">$MASTER_HOSTNAME</span>;</span></span><br><span class="line"><span class="string">rabbitmqctl start_app;</span></span><br><span class="line"><span class="string">rabbitmqctl cluster_status;</span></span><br><span class="line"><span class="string">&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Setup RabbitMQ Slaves&quot;</span>;</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;=====================&quot;</span>;</span><br><span class="line"></span><br><span class="line">SERVER=$(getHostname <span class="string">&quot;Enter slave&#x27;s hostname or &#x27;q&#x27; to quit&quot;</span>);</span><br><span class="line"><span class="keyword">while</span> <span class="built_in">test</span> <span class="string">&quot;<span class="variable">$SERVER</span>&quot;</span> != <span class="string">&quot;q&quot;</span></span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;Setting up slave&quot;</span>;</span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;ssh &#x27;<span class="variable">$SERVER</span>&#x27;&quot;</span>;</span><br><span class="line">  ssh -t <span class="variable">$SERVER</span> <span class="string">&quot;bash -c &#x27;<span class="variable">$SETUP_SLAVE_SCRIPT</span>&#x27;&quot;</span>;</span><br><span class="line">  SERVER=$(getHostname <span class="string">&quot;Enter another slave&#x27;s hostname or &#x27;q&#x27; to quit&quot;</span>);</span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Step 3 : Create admin user</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="variable">$MASTER_HOSTNAME</span>] Setting up admin user&quot;</span>;</span><br><span class="line">ssh -t <span class="variable">$MASTER_HOSTNAME</span> <span class="string">&quot;bash -c &#x27;rabbitmqctl add_user admin password&#x27;&quot;</span>;</span><br><span class="line">ssh -t <span class="variable">$MASTER_HOSTNAME</span> <span class="string">&quot;bash -c &#x27;rabbitmqctl set_user_tags admin administrator&#x27;&quot;</span>;</span><br><span class="line">ssh -t <span class="variable">$MASTER_HOSTNAME</span> <span class="string">&quot;bash -c &#x27;rabbitmqctl set_permissions -p / admin \&quot;.*\&quot; \&quot;.*\&quot; \&quot;.*\&quot;&#x27;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Step 3 : Delete guest user</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="variable">$MASTER_HOSTNAME</span>] Removing user&quot;</span>;</span><br><span class="line">ssh -t <span class="variable">$MASTER_HOSTNAME</span> <span class="string">&quot;bash -c &#x27;rabbitmqctl delete_user guest&#x27;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Step 5 : Create sync policy</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[<span class="variable">$MASTER_HOSTNAME</span>] Synchronizing cluster&quot;</span>;</span><br><span class="line">ssh -t <span class="variable">$MASTER_HOSTNAME</span> $<span class="string">&quot;bash -c &#x27;rabbitmqctl set_policy -p / ha-all \&quot;\&quot; &#x27;\&#x27;&#x27;&#123;\&quot;ha-mode\&quot;:\&quot;all\&quot;,\&quot;ha-sync-mode\&quot;:\&quot;automatic\&quot;&#125;&#x27;\&#x27;&#x27;&#x27;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Done&quot;</span>;</span><br></pre></td></tr></table></figure><h2 id="常见问题">常见问题</h2><h3 id="rabbitmq-异常退出无法重启">1. RabbitMQ 异常退出无法重启</h3><p><strong>问题描述：</strong></p><p>机房突然停电，rabbitmq 的主机异常断电，集群服务全部需要重启。但是在执行 <code>systemctl start rabbitmq-server</code> 启动主节点服务的时候，没有反应，服务没有启动，命令执行卡住。只能 Ctrl+C 结束进程。</p><p>查看 /var/log/rabbitmq/rabbit@hostname.log 发现有如下报错信息：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">2021-04-13 20:15:00.011 [info] &lt;0.317.0&gt; Waiting for Mnesia tables for 30000 ms, 9 retries left</span><br><span class="line">2021-04-13 20:15:00.011 [warning] &lt;0.317.0&gt; Error while waiting for Mnesia tables: &#123;failed_waiting_for_tables,&#123;node_not_running,rabbit@node233&#125;&#125;</span><br><span class="line">...</span><br><span class="line">2021-04-13 20:15:00.012 [info] &lt;0.317.0&gt; Waiting for Mnesia tables for 30000 ms, 0 retries left</span><br><span class="line">2021-04-13 20:15:00.013 [error] &lt;0.316.0&gt; CRASH REPORT Process &lt;0.316.0&gt; with 0 neighbours exited with reason: &#123;&#123;failed_waiting_for_tables,&#123;node_not_running,rabbit@node233&#125;&#125;,&#123;rabbit,start,[normal,[]]&#125;&#125; in application_master:init/4 line 138</span><br></pre></td></tr></table></figure><p><strong>解决方法：</strong></p><ol type="1"><li>检查端口是否被占用；</li><li>检查 /var/log/rabbitmq 目录权限；</li><li>分布式数据库 mnesia 异常，将 /var/lib/rabbitmq/mnesia/rabbit@hostname/ 下的数据库文件清空即可，重新启动服务；或直接清除 /var/lib/rabbitmq/mnesia 目录下所有文件；</li></ol><h2 id="references">References</h2><p><a href="https://gist.github.com/fernandoaleman/fe34e83781f222dfd8533b36a52dddcc">Install RabbitMQ on CentOS 7</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了在 CentOS7 系统下安装 RabbitMQ 的详细步骤。首先需要安装 Erlang 和 RabbitMQ，然后进行 RabbitMQ 的配置，包括设置防火墙和 SELinux。接着介绍了如何启用 RabbitMQ Web 控制台以及如何配置 SSL 证书。最后，文章提供了设置 RabbitMQ 集群的脚本 /usr/local/sbin/rabbitmq-cluster.sh，供读者参考。&lt;/p&gt;</summary>
    
    
    
    <category term="middleware" scheme="https://www.wylu.me/categories/middleware/"/>
    
    <category term="mq" scheme="https://www.wylu.me/categories/middleware/mq/"/>
    
    <category term="RabbitMQ" scheme="https://www.wylu.me/categories/middleware/mq/RabbitMQ/"/>
    
    
    <category term="rabbitmq" scheme="https://www.wylu.me/tags/rabbitmq/"/>
    
  </entry>
  
  <entry>
    <title>pycharm 远程调试</title>
    <link href="https://www.wylu.me/posts/a02cc5ac/"/>
    <id>https://www.wylu.me/posts/a02cc5ac/</id>
    <published>2023-07-25T15:31:21.000Z</published>
    <updated>2023-07-25T15:59:11.403Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了使用 PyCharm 提供的 pydevd 模块进行远程调试的步骤和原理。首先，在远程服务器上安装所需依赖，然后在本地机器上配置监听的 IP 地址和端口号，确保远程计算机可以访问到本地 IP 地址。接下来，在远程计算机的代码中插入特定的代码，其中 IP 地址和端口号需要与 PyCharm 中的监听配置一致。最后，通过 PyCharm 的远程调试模式，将 PyCharm 作为服务端，远程计算机上的应用程序作为客户端，建立连接并进行单步调试。</p><span id="more"></span><h1 id="pycharm-远程调试">pycharm 远程调试</h1><p><a href="https://www.jetbrains.com/help/pycharm/remote-debugging-with-product.html" class="uri">https://www.jetbrains.com/help/pycharm/remote-debugging-with-product.html</a></p><h2 id="远程服务器安装依赖">1. 远程服务器安装依赖</h2><h3 id="下载依赖包">下载依赖包</h3><p><a href="https://pypi.org/project/pydevd-pycharm" class="uri">https://pypi.org/project/pydevd-pycharm</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip download -d Downloads pydevd-pycharm==221.5921.27</span><br></pre></td></tr></table></figure><h3 id="安装依赖包">安装依赖包</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">tar -zxf pydevd-pycharm-221.5921.27.tar.gz</span><br><span class="line">cd pydevd-pycharm-221.5921.27/</span><br><span class="line">python3 setup.py install</span><br></pre></td></tr></table></figure><h4 id="查看依赖是否安装成功">查看依赖是否安装成功</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# pip3 list | grep pycharm</span><br><span class="line">pydevd-pycharm      221.5921.27</span><br></pre></td></tr></table></figure><h2 id="本地机器的配置">2. 本地机器的配置</h2><ul><li>Run -&gt; Edit Configurations</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/1fa98965e687d1f60ebea828c61bd997.png" alt="Run -&gt; Edit Configurations" /></p><ul><li>Add New <a href="https://so.csdn.net/so/search?q=Configuration&amp;spm=1001.2101.3001.7020">Configuration</a> -&gt; Python Remote Debug</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/333d31a77d6c2fe4f7bc54620722bed4.png" alt="Add New Configuration -&gt; Python Remote Debug" /></p><p>填写 <strong>Local host name</strong> 和 <strong>Port</strong>，其中 Local host name 指的是本机开发环境的 IP 地址，而 Port 则随便填写一个 10000 以上的即可；需要注意的是，由于远程计算机需要连接至本地开发环境，因此本地 IP 地址应该保证远程可以访问得到。</p><h2 id="远程计算机的代码配置">3. 远程计算机的代码配置</h2><p>在远程需要调试的代码中插入如下代码：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pydevd</span><br><span class="line">pydevd.settrace(<span class="string">&#x27;10.214.161.179&#x27;</span>, port=<span class="number">10000</span>, stdoutToServer=<span class="literal">True</span>, stderrToServer=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><p>其中，IP 地址和端口号要与 PyCharm 中的监听配置保持一致。</p><h2 id="pycharm-远程调试的原理">4. pycharm 远程调试的原理</h2><p>在远程调试的模式下，PyCharm（IDE）扮演服务端（Server）的角色，而运行在远程计算机上的应用程序扮演客户端（Client）的角色。正因如此，进行远程调试时，需要先在本地开发环境中设定端口并启动IDE，IDE会对设定的端口开始监听，等待客户端的连接请求；</p><p>针对远程调试功能，PyCharm 提供了 pydevd 模块，该模块以 pycharm-debug.egg 的形式存在于 PyCharm 的安装路径中。远程计算机安装该库文件后，然后就可以调用 pydevd.settrace 方法，该方法会指定 IDE 所在机器的 IP 地址和监听的端口号，用于与 IDE 建立连接；建立连接后，便可在 IDE 中对远程在远程计算机中的程序进行单步调试。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了使用 PyCharm 提供的 pydevd 模块进行远程调试的步骤和原理。首先，在远程服务器上安装所需依赖，然后在本地机器上配置监听的 IP 地址和端口号，确保远程计算机可以访问到本地 IP 地址。接下来，在远程计算机的代码中插入特定的代码，其中 IP 地址和端口号需要与 PyCharm 中的监听配置一致。最后，通过 PyCharm 的远程调试模式，将 PyCharm 作为服务端，远程计算机上的应用程序作为客户端，建立连接并进行单步调试。&lt;/p&gt;</summary>
    
    
    
    <category term="tool" scheme="https://www.wylu.me/categories/tool/"/>
    
    <category term="pycharm" scheme="https://www.wylu.me/categories/tool/pycharm/"/>
    
    
    <category term="pycharm" scheme="https://www.wylu.me/tags/pycharm/"/>
    
  </entry>
  
  <entry>
    <title>Docker 磁盘清理</title>
    <link href="https://www.wylu.me/posts/ab2a96fe/"/>
    <id>https://www.wylu.me/posts/ab2a96fe/</id>
    <published>2023-07-23T15:47:20.000Z</published>
    <updated>2023-07-23T15:50:37.380Z</updated>
    
    <content type="html"><![CDATA[<p>在使用 docker 的时候，经常需要下载镜像，给镜像打标签，然后又因为没有及时清理无用的镜像，久而久之，docker 就会把宿主机的磁盘空间占满，导致系统异常。对于 docker 磁盘占用的问题，可以通过 docker 自带的 prune 命令清理或者迁移 docker 下的 overlay2 文件夹来解决。</p><span id="more"></span><h1 id="docker-磁盘清理">Docker 磁盘清理</h1><h2 id="通过-prune-清理">通过 prune 清理</h2><h3 id="查看磁盘使用情况">查看磁盘使用情况</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">du -sh /var/lib/docker</span><br></pre></td></tr></table></figure><h3 id="查看-docker-磁盘占用">查看 docker 磁盘占用</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker system df</span><br></pre></td></tr></table></figure><p>该命令可以用于查看 Docker 环境中的磁盘使用情况，包括镜像、容器、数据卷和网络等资源的使用情况。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker system df -v</span><br></pre></td></tr></table></figure><p>与 <code>docker system df</code> 命令不同的是，该命令可以进一步查看空间占用细节，以确定是哪个镜像、容器或本地卷占用过高空间。</p><h3 id="清理-docker-磁盘占用">清理 docker 磁盘占用</h3><h4 id="自动清理">自动清理</h4><p>如果您只想清理未被使用的容器、数据卷和网络等资源，可以使用以下命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker system prune</span><br></pre></td></tr></table></figure><p>该命令会清理未被使用的容器、数据卷和网络等资源，但不会删除未被使用的镜像。</p><blockquote><p>该命令所清理的对象如下：</p><ul><li>已停止的容器</li><li>未被任何容器使用的卷</li><li>未被任何容器所关联的网络</li><li>所有悬空的镜像</li></ul><p>对于上面提到的一些镜像或容器的状态所代表的含义如下：</p><ul><li>已使用的镜像：指所有已被容器（包括 stop 的）关联的镜像，也就是 <code>docker ps -a</code> 所看到的所有容器对应的 image</li><li>未引用镜像：没有被分配或使用在容器中的镜像</li><li>悬空镜像（dangling image）：未配置任何 tag（也就是无法被引用）的镜像。通常是由于镜像编译过程中未指定 <code>-t</code> 参数配置 tag 导致的。</li></ul></blockquote><p>如果想清理所有未被使用的镜像，可以使用 <code>-a</code> 参数：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker system prune -a</span><br></pre></td></tr></table></figure><p>该命令会提示您确认是否要清理无用资源，确认后将会删除所有无用资源（包括未被使用的镜像、已停止的容器、未被使用的数据卷和网络等），释放磁盘空间。请注意，该命令会删除所有未被使用的镜像，包括您手动下载的镜像和 Docker 官方镜像，因此请谨慎使用。</p><p>删除无用的容器：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker container prune</span><br></pre></td></tr></table></figure><ul><li><p>默认情况下 <code>docker container prune</code> 会清理掉所有处于 stopped 状态的容器</p></li><li><p>如果不想都删掉，也可以使用 <code>--filter</code> 标志来筛选出不希望被清理掉的容器</p><p>例子：清除掉所有停掉的容器，但24内创建的除外。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker container prune --filter &quot;until=24h&quot;</span><br></pre></td></tr></table></figure></li></ul><p>删除无用的卷：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker volume prune</span><br></pre></td></tr></table></figure><p>删除无用的网络：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker network prune</span><br></pre></td></tr></table></figure><h4 id="手动清理">手动清理</h4><p>（1）删除所有悬空镜像，不删除未使用镜像：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker rmi $(docker images -f &quot;dangling=true&quot; -q)</span><br></pre></td></tr></table></figure><p>（2）删除所有未使用镜像和悬空镜像</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker rmi $(docker images -q)</span><br></pre></td></tr></table></figure><p>（3）删除所有未被容器引用的卷</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker volume rm $(docker volume ls -qf dangling=true)</span><br></pre></td></tr></table></figure><p>如果卷占用空间过高，可以清除一些不使用的卷，包括一些未被任何容器调用的卷（<code>-v</code> 详细信息中若显示 LINKS = 0，则是未被调用）</p><p>（4）删除容器</p><p>删除所有已退出的容器：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker rm -v $(docker ps -aq -f status=exited)</span><br></pre></td></tr></table></figure><p>具体来说，该命令使用了以下参数：</p><ul><li><code>-a</code>：列出所有的容器，包括正在运行的容器和已经停止的容器。</li><li><code>-q</code>：只列出容器的 ID，而不显示容器的详细信息。</li><li><code>-f status=exited</code>：只列出状态为 "exited" 的容器，即已经退出的容器。</li></ul><p>删除所有状态为 dead 的容器：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker rm -v $(docker ps -aq -f status=dead)</span><br></pre></td></tr></table></figure><p>如果发现是容器占用过高的空间，可以手动删除一些。</p><h2 id="迁移-varlibdocker">迁移 /var/lib/docker</h2><p>/var/lib/docker/overlay2 占用很大，要想清理 Docker 占用的磁盘空间，可以考虑迁移 /var/lib/docker 目录。</p><h3 id="停止-docker-服务">停止 docker 服务</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl stop docker</span><br></pre></td></tr></table></figure><h3 id="创建新的分区目录">创建新的分区目录</h3><p>创建一个磁盘空间更大的分区目录，例如：/home/lib/docker</p><h3 id="迁移-varlibdocker-1">迁移 /var/lib/docker</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rsync -avz /var/lib/docker /home/lib/docker</span><br></pre></td></tr></table></figure><h3 id="配置-devicemapper.conf">配置 devicemapper.conf</h3><p>配置 /etc/systemd/system/docker.service.d/devicemapper.conf（查看 devicemapper.conf 是否存在，如果不存在则创建）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /etc/systemd/system/docker.service.d/</span><br><span class="line">vim /etc/systemd/system/docker.service.d/devicemapper.conf</span><br></pre></td></tr></table></figure><p>配置内容如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[Service]</span><br><span class="line">ExecStart=/usr/bin/dockerd --graph=/home/lib/docker/docker</span><br></pre></td></tr></table></figure><h3 id="重启-docker">重启 docker</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl restart docker</span><br></pre></td></tr></table></figure><h3 id="验证">验证</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker info</span><br></pre></td></tr></table></figure><p>命令检查 Docker 的根目录，它将被更改为 /home/lib/docker/docker</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">Docker Root Dir: /home/lib/docker/docker</span><br><span class="line">Debug Mode (client): false</span><br><span class="line">Debug Mode (server): false</span><br><span class="line">Registry: https://index.docker.io/v1/</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>查看之前的镜像是否还在：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# docker images</span><br><span class="line">REPOSITORY                  TAG       IMAGE ID       CREATED         SIZE</span><br><span class="line">demo                        0.0.1     4fa4c5aae26d   4 months ago    378MB</span><br><span class="line">adoptopenjdk/openjdk11      alpine    552165ab2add   4 months ago    343MB</span><br><span class="line">registry                    2.8.1     dcb3d42c1744   5 months ago    24.1MB</span><br></pre></td></tr></table></figure><p>确定容器没问题后删除 /var/lib/docker 目录下的文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rm -rf /var/lib/docker/*</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;在使用 docker 的时候，经常需要下载镜像，给镜像打标签，然后又因为没有及时清理无用的镜像，久而久之，docker 就会把宿主机的磁盘空间占满，导致系统异常。对于 docker 磁盘占用的问题，可以通过 docker 自带的 prune 命令清理或者迁移 docker 下的 overlay2 文件夹来解决。&lt;/p&gt;</summary>
    
    
    
    <category term="cloud" scheme="https://www.wylu.me/categories/cloud/"/>
    
    <category term="docker" scheme="https://www.wylu.me/categories/cloud/docker/"/>
    
    
    <category term="docker" scheme="https://www.wylu.me/tags/docker/"/>
    
  </entry>
  
  <entry>
    <title>CentOS 搭建 DNS 服务</title>
    <link href="https://www.wylu.me/posts/b769518c/"/>
    <id>https://www.wylu.me/posts/b769518c/</id>
    <published>2023-04-08T02:45:39.000Z</published>
    <updated>2023-07-24T15:08:32.759Z</updated>
    
    <content type="html"><![CDATA[<p>本文介绍了在 CentOS 上使用 BIND (Berkeley Internet Name Domain) 搭建 DNS 服务的步骤。首先需要下载 DNS 服务软件，然后配置主配置文件和区域文件，接着配置正向解析数据文件和反向解析数据文件，设置文件权限，检查配置文件是否配置正确，最后启动 DNS 服务并测试解析。</p><span id="more"></span><h1 id="centos-搭建-dns-服务">CentOS 搭建 DNS 服务</h1><p>可以使用 BIND (Berkeley Internet Name Domain) 来搭建 DNS 服务。以下是在 CentOS 上搭建 BIND 的步骤：</p><h2 id="下载-dns-服务软件">下载 DNS 服务软件</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">服务端需安装 bind-chroot 软件</span></span><br><span class="line">yun install -y bind-chroot</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">客户端需下载 bind-utils（需要使用 nslookup 命令）</span></span><br><span class="line">yum install -y bind-utils</span><br></pre></td></tr></table></figure><h2 id="配置主配置文件">配置主配置文件</h2><p><a href="https://bind9.readthedocs.io/en/v9_18_8/reference.html#configuration-reference" class="uri">https://bind9.readthedocs.io/en/v9_18_8/reference.html#configuration-reference</a></p><p>编辑 /etc/named.conf 文件，修改以下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">options &#123;</span><br><span class="line">        // 配置 DNS 服务监听的 IP 地址（可以写成 any）和端口（默认 53，确保该端口没有被占用）</span><br><span class="line">        listen-on port 53 &#123; any; &#125;;</span><br><span class="line">        // 允许任何网段的主机访问 DNS 服务</span><br><span class="line">        allow-query     &#123; any; &#125;;</span><br><span class="line"></span><br><span class="line">        dnssec-enable no;</span><br><span class="line">        dnssec-validation no;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><a href="https://web.mit.edu/rhel-doc/4/RH-DOCS/rhel-rg-en-4/s1-bind-namedconf.html" class="uri">https://web.mit.edu/rhel-doc/4/RH-DOCS/rhel-rg-en-4/s1-bind-namedconf.html</a></p><h2 id="配置区域文件">配置区域文件</h2><p>编辑 /etc/named.rfc1912.zones 文件，添加以下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">zone &quot;wylu.test&quot; IN &#123;</span><br><span class="line">        type master;</span><br><span class="line">        file &quot;wylu.test.localhost&quot;;</span><br><span class="line">        allow-update &#123; none; &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">zone &quot;170.128.10.in-addr.arpa&quot; IN &#123;</span><br><span class="line">        type master;</span><br><span class="line">        file &quot;wylu.test.loopback&quot;;</span><br><span class="line">        allow-update &#123; none; &#125;;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="配置正向解析数据文件">配置正向解析数据文件</h2><p>拷贝一份正向解析数据文件的模板，然后进行编辑：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd /var/named</span><br><span class="line">cp -a named.localhost wylu.test.localhost</span><br><span class="line">vim wylu.test.localhost</span><br></pre></td></tr></table></figure><p>添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$TTL 1D</span><br><span class="line">@       IN SOA  wylu.test. rname.invalid. (</span><br><span class="line">                                        0       ; serial</span><br><span class="line">                                        1D      ; refresh</span><br><span class="line">                                        1H      ; retry</span><br><span class="line">                                        1W      ; expire</span><br><span class="line">                                        3H )    ; minimum</span><br><span class="line">        NS      www.wylu.test.</span><br><span class="line">www     A       10.128.170.235</span><br><span class="line">email   A       10.128.170.235</span><br></pre></td></tr></table></figure><h2 id="配置反向解析数据文件">配置反向解析数据文件</h2><p>拷贝一份正向解析数据文件的模板，然后进行编辑：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd /var/named</span><br><span class="line">cp -a named.loopback wylu.test.loopback</span><br><span class="line">vim wylu.test.loopback</span><br></pre></td></tr></table></figure><p>添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$TTL 1D</span><br><span class="line">@       IN SOA  wylu.test. rname.invalid. (</span><br><span class="line">                                        0       ; serial</span><br><span class="line">                                        1D      ; refresh</span><br><span class="line">                                        1H      ; retry</span><br><span class="line">                                        1W      ; expire</span><br><span class="line">                                        3H )    ; minimum</span><br><span class="line">        NS      www.wylu.test.</span><br><span class="line">235     PTR     www.wylu.test.</span><br></pre></td></tr></table></figure><h2 id="设置文件权限">设置文件权限</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chown named:named wylu.test.localhost wylu.test.loopback</span><br></pre></td></tr></table></figure><p><strong>注意：请确保 wylu.test.localhost 和 wylu.test.loopback 文件的属主和属组为 named。</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost named]# ls -alh</span><br><span class="line">total 28K</span><br><span class="line">drwxrwx--T.  5 root  named  178 Mar 22 18:52 .</span><br><span class="line">drwxr-xr-x. 22 root  root  4.0K Mar 22 17:34 ..</span><br><span class="line">drwxrwx---.  2 named named   23 Mar 22 17:45 data</span><br><span class="line">drwxrwx---.  2 named named   60 Mar 22 18:52 dynamic</span><br><span class="line">-rw-r-----.  1 root  named 2.3K Nov  8 19:18 named.ca</span><br><span class="line">-rw-r-----.  1 root  named  152 Nov  8 19:18 named.empty</span><br><span class="line">-rw-r-----.  1 root  named  152 Nov  8 19:18 named.localhost</span><br><span class="line">-rw-r-----.  1 root  named  168 Nov  8 19:18 named.loopback</span><br><span class="line">drwxrwx---.  2 named named    6 Nov  8 19:18 slaves</span><br><span class="line">-rw-r--r--.  1 root  root   429 Mar 22 18:51 wylu.test.localhost</span><br><span class="line">-rw-r--r--.  1 root  root   397 Mar 22 17:44 wylu.test.loopback</span><br></pre></td></tr></table></figure><p>否则启动服务时会产生如下错误提示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Mar 22 17:44:08 localhost.localdomain named[15726]: zone 170.128.10.in-addr.arpa/IN: loading from master file wylu.test.loopback failed: permission denied</span><br><span class="line">Mar 22 17:44:08 localhost.localdomain named[15726]: zone 170.128.10.in-addr.arpa/IN: not loaded due to errors.</span><br><span class="line">Mar 22 17:44:08 localhost.localdomain named[15726]: zone wylu.test/IN: loading from master file wylu.test.localhost failed: permission denied</span><br><span class="line">Mar 22 17:44:08 localhost.localdomain named[15726]: zone wylu.test/IN: not loaded due to errors.</span><br><span class="line">Mar 22 17:44:08 localhost.localdomain named[15726]: all zones loaded</span><br><span class="line">Mar 22 17:44:08 localhost.localdomain named[15726]: running</span><br></pre></td></tr></table></figure><h2 id="检查配置文件是否配置正确">检查配置文件是否配置正确</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost named]# named-checkconf -z /etc/named.conf</span><br><span class="line">zone localhost.localdomain/IN: loaded serial 0</span><br><span class="line">zone localhost/IN: loaded serial 0</span><br><span class="line">zone 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa/IN: loaded serial 0</span><br><span class="line">zone 1.0.0.127.in-addr.arpa/IN: loaded serial 0</span><br><span class="line">zone 0.in-addr.arpa/IN: loaded serial 0</span><br><span class="line">zone wylu.test/IN: loaded serial 0</span><br><span class="line">zone 170.128.10.in-addr.arpa/IN: loaded serial 0</span><br></pre></td></tr></table></figure><p>如上输出表示配置文件没问题。</p><h2 id="启动-dns-服务">启动 DNS 服务</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">关闭防火墙服务</span></span><br><span class="line">systemctl stop firewalld</span><br><span class="line">systemctl disable firewalld</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">临时禁用 SELinux</span></span><br><span class="line">setenforce 0</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">永久禁用 SELinux</span></span><br><span class="line">vim /etc/selinux/config</span><br><span class="line">SELINUX=disabled</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">启动 DNS 服务</span></span><br><span class="line">systemctl start named</span><br><span class="line">systemctl enable named</span><br></pre></td></tr></table></figure><p>查看 DNS 服务监听情况：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost named]# netstat -anupl | grep 53</span><br><span class="line">udp        0      0 172.17.0.1:53           0.0.0.0:*           2111795/named</span><br><span class="line">udp        0      0 10.128.170.235:53       0.0.0.0:*           2111795/named</span><br><span class="line">udp        0      0 127.0.0.1:53            0.0.0.0:*           2111795/named</span><br><span class="line">udp6       0      0 ::1:53                  :::*                2111795/named</span><br></pre></td></tr></table></figure><h2 id="测试解析">测试解析</h2><h3 id="正向解析">正向解析</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost named]# nslookup email.wylu.test 10.128.170.235</span><br><span class="line">Server:         10.128.170.235</span><br><span class="line">Address:        10.128.170.235#53</span><br><span class="line"></span><br><span class="line">Name:   email.wylu.test</span><br><span class="line">Address: 10.128.170.235</span><br></pre></td></tr></table></figure><h3 id="反向解析">反向解析</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost named]# nslookup 10.128.170.235 10.128.170.235</span><br><span class="line">235.170.128.10.in-addr.arpa     name = www.wylu.test.</span><br></pre></td></tr></table></figure><h2 id="references">References</h2><h3 id="cp--a-选项作用">cp -a 选项作用</h3><p>cp -a 是 cp 命令的一个选项，用于复制文件和目录，并保留所有的属性和权限。具体来说，-a 选项等价于以下三个选项的组合：</p><ul><li><code>-p</code> 选项：保留文件的权限、所有者和时间戳。</li><li><code>-R</code> 选项：递归复制目录及其内容。</li><li><code>-d</code> 选项：保留符号链接的属性。</li></ul><p>使用 cp -a 命令可以在复制文件和目录时保留所有的属性和权限，包括文件的所有者、组、权限、时间戳等。这对于备份和迁移文件和目录非常有用。 例如，要将目录 /home/user1 复制到目录 /home/user2，可以使用以下命令：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp -a /home/user1 /home/user2</span><br></pre></td></tr></table></figure><p>这将递归地复制 /home/user1 目录及其所有内容到 /home/user2 目录，并保留所有的属性和权限。</p><h3 id="禁用-selinux">禁用 SELinux</h3><p>SELinux（Security-Enhanced Linux）是一个安全子系统，用于限制进程和用户的访问权限，以提高系统的安全性。但是，在某些情况下，SELinux 可能会导致一些问题，例如阻止某些进程或服务的正常运行。如果你需要关闭 SELinux，可以按照以下步骤操作：</p><h3 id="检查-selinux-的状态">检查 SELinux 的状态</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sestatus</span><br></pre></td></tr></table></figure><p>如果 SELinux 处于启用状态，将显示以下输出：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[root@localhost named]# sestatus</span><br><span class="line">SELinux status:                 enabled</span><br><span class="line">SELinuxfs mount:                /sys/fs/selinux</span><br><span class="line">SELinux root directory:         /etc/selinux</span><br><span class="line">Loaded policy name:             targeted</span><br><span class="line">Current mode:                   enforcing</span><br><span class="line">Mode from config file:          enforcing</span><br><span class="line">Policy MLS status:              enabled</span><br><span class="line">Policy deny_unknown status:     allowed</span><br><span class="line">Memory protection checking:     actual (secure)</span><br><span class="line">Max kernel policy version:      33</span><br></pre></td></tr></table></figure><h3 id="临时禁用-selinux">临时禁用 SELinux</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">setenforce 0</span><br></pre></td></tr></table></figure><p>这将把 SELinux 的模式从强制模式（enforcing）切换到宽容模式（permissive），并在控制台上显示警告信息。在宽容模式下，SELinux 仍然会记录违规行为，但不会阻止它们。</p><h3 id="永久禁用-selinux">永久禁用 SELinux</h3><p>要永久禁用 SELinux，需要编辑 /etc/selinux/config 文件，并将 SELINUX 的值设置为 disabled。可以使用以下命令打开该文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/selinux/config</span><br></pre></td></tr></table></figure><p>找到以下行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELINUX=enforcing</span><br></pre></td></tr></table></figure><p>将其改为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELINUX=disabled</span><br></pre></td></tr></table></figure><p>保存并关闭文件。</p><h3 id="重启系统">重启系统</h3><p>为了使 SELinux 的更改生效，需要重启系统：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">reboot</span><br></pre></td></tr></table></figure><p>在系统重新启动后，SELinux 将被永久禁用。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文介绍了在 CentOS 上使用 BIND (Berkeley Internet Name Domain) 搭建 DNS 服务的步骤。首先需要下载 DNS 服务软件，然后配置主配置文件和区域文件，接着配置正向解析数据文件和反向解析数据文件，设置文件权限，检查配置文件是否配置正确，最后启动 DNS 服务并测试解析。&lt;/p&gt;</summary>
    
    
    
    <category term="os" scheme="https://www.wylu.me/categories/os/"/>
    
    <category term="centos" scheme="https://www.wylu.me/categories/os/centos/"/>
    
    
    <category term="centos" scheme="https://www.wylu.me/tags/centos/"/>
    
  </entry>
  
  <entry>
    <title>二进制部署 k8s 集群 1.23.6 版本</title>
    <link href="https://www.wylu.me/posts/b4d8958f/"/>
    <id>https://www.wylu.me/posts/b4d8958f/</id>
    <published>2023-04-06T16:30:31.000Z</published>
    <updated>2023-08-07T16:22:58.192Z</updated>
    
    <content type="html"><![CDATA[<p>通过本文的指导，读者可以了解如何通过二进制的方式部署 Kubernetes 1.23.6 版本集群。二进制部署可以加深对 Kubernetes 各组件的理解，可以灵活地将各个组件部署到不同的机器，以满足自身的要求。但是需要注意的是，二进制部署需要手动配置各个组件，需要一定的技术水平和经验。</p><span id="more"></span><h1 id="二进制部署-k8s-集群-1.23.6-版本">二进制部署 k8s 集群 1.23.6 版本</h1><h2 id="环境介绍">1. 环境介绍</h2><p>虽然 <a href="https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/">kubeadm</a>, <a href="https://kubernetes.io/zh/docs/setup/production-environment/tools/kops/">kops</a>, <a href="https://kubernetes.io/zh/docs/setup/production-environment/tools/kubespray/">kubespray</a> 以及 <a href="https://docs.rancher.cn/rke/">rke</a>, <a href="https://kubesphere.io/zh/docs/">kubesphere</a> 等工具可以快速部署 K8s 集群，但是依然会有很多人热衷与使用二进制部署 K8s 集群。</p><p>二进制部署可以加深对 K8s 各组件的理解，可以灵活地将各个组件部署到不同的机器，以满足自身的要求。还可以生成一个超长时间自签证书，比如 99 年，免去忘记更新证书过期带来的生产事故。</p><h3 id="书写约定">1.1 书写约定</h3><ul><li>命令行输入，均以 <code>➜</code> 符号表示</li><li>注释使用 <code>#</code> 或 <code>//</code> 表示</li><li>执行命令输出结果，以空行分隔</li></ul><h3 id="规划">1.2 规划</h3><table><colgroup><col style="width: 4%" /><col style="width: 4%" /><col style="width: 4%" /><col style="width: 86%" /></colgroup><thead><tr class="header"><th>角色</th><th>主机名</th><th>IP</th><th>组件</th></tr></thead><tbody><tr class="odd"><td>master</td><td>cnode0</td><td>10.128.170.20</td><td>etcd, kube-apiserver, kube-controller-manager, kube-scheduler, kube-proxy, kubelet</td></tr><tr class="even"><td>node1</td><td>cnode1</td><td>10.128.170.21</td><td>kubelet, kube-proxy</td></tr></tbody></table><h3 id="环境配置">1.3 环境配置</h3><ul><li><p>关闭防火墙</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl stop firewalld</span><br><span class="line">➜ systemctl disable firewalld</span><br></pre></td></tr></table></figure></li><li><p>关闭 selinux</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">临时</span></span><br><span class="line">➜ setenforce 0</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">永久</span></span><br><span class="line">➜ sed -i &#x27;s/enforcing/disabled/&#x27; /etc/selinux/config</span><br></pre></td></tr></table></figure></li><li><p>关闭 swap</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">临时</span></span><br><span class="line">➜ swapoff -a</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">永久</span></span><br><span class="line">➜ sed -ri &#x27;s/.*swap.*/#&amp;/&#x27; /etc/fstab</span><br></pre></td></tr></table></figure><p>使用 <code>-r</code> 选项可以使用扩展正则表达式，这提供了一种更强大和灵活的方式来匹配文本中的模式。</p><p>使用正则表达式 <code>.*swap.*</code> 匹配包含 swap 字符串的行，并在行首添加 # 符号，&amp; 表示匹配到的整个字符串。</p></li><li><p>设置主机名</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">10.128.170.20 主机</span></span><br><span class="line">➜ hostnamectl set-hostname cnode0</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">10.128.170.21 主机</span></span><br><span class="line">➜ hostnamectl set-hostname cnode1</span><br></pre></td></tr></table></figure></li><li><p>时间同步</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">设置时区</span></span><br><span class="line">➜ timedatectl set-timezone Asia/Shanghai</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">安装时间同步服务</span></span><br><span class="line">➜ yum install chrony -y</span><br><span class="line">➜ systemctl enable --now chronyd</span><br></pre></td></tr></table></figure></li><li><p>主机名解析</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt;&gt; /etc/hosts &lt;&lt; EOF</span><br><span class="line">10.128.170.20 cnode0 cnode0.com</span><br><span class="line">10.128.170.21 cnode1 cnode1.com</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure></li><li><p>将桥接的 IPv4 流量传递到 iptables 的链</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/sysctl.d/kubernetes.conf &lt;&lt; EOF</span><br><span class="line">net.bridge.bridge-nf-call-ip6tables = 1</span><br><span class="line">net.bridge.bridge-nf-call-iptables = 1</span><br><span class="line">net.ipv4.ip_forward = 1</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">生效</span></span><br><span class="line">➜ sysctl --system</span><br></pre></td></tr></table></figure></li><li><p>设置文件描述符限制</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">临时</span></span><br><span class="line">➜ ulimit -SHn 65535</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">永久</span></span><br><span class="line">➜ echo &quot;*      -      nofile   65535&quot; &gt;&gt;/etc/security/limits.conf</span><br></pre></td></tr></table></figure><p>用于设置当前用户的最大文件描述符数限制。具体来说，它的作用是将当前用户的软限制和硬限制都设置为 65535。</p></li><li><p>更新 epel 源</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ yum install epel-release -y</span><br></pre></td></tr></table></figure><p>如需换源，请参考：<a href="https://developer.aliyun.com/mirror/epel" class="uri">https://developer.aliyun.com/mirror/epel</a></p></li><li><p>加载 ipvs 模块</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ yum install ipset ipvsadm -y</span><br><span class="line">➜ cat &gt; /etc/sysconfig/modules/ipvs.modules &lt;&lt; &quot;EOF&quot;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/bash</span></span><br><span class="line">modprobe -- ip_vs</span><br><span class="line">modprobe -- ip_vs_rr</span><br><span class="line">modprobe -- ip_vs_wrr</span><br><span class="line">modprobe -- ip_vs_sh</span><br><span class="line">modprobe -- nf_conntrack_ipv4</span><br><span class="line">EOF</span><br><span class="line">➜ chmod +x /etc/sysconfig/modules/ipvs.modules</span><br><span class="line">➜ /bin/bash /etc/sysconfig/modules/ipvs.modules</span><br><span class="line">➜ lsmod | grep -e ip_vs -e nf_conntrack_ipv4</span><br></pre></td></tr></table></figure><ul><li>modprobe -- ip_vs: 加载 ip_vs 内核模块，该模块提供了 Linux 内核中的 IP 负载均衡功能。</li><li>modprobe -- ip_vs_rr: 加载 ip_vs_rr 内核模块，该模块提供了基于轮询算法的 IP 负载均衡策略。</li><li>modprobe -- ip_vs_wrr: 加载 ip_vs_wrr 内核模块，该模块提供了基于加权轮询算法的 IP 负载均衡策略。</li><li>modprobe -- ip_vs_sh: 加载 ip_vs_sh 内核模块，该模块提供了基于哈希算法的 IP 负载均衡策略。</li><li>modprobe -- nf_conntrack_ipv4: 加载 nf_conntrack_ipv4 内核模块，该模块提供了 Linux 内核中的网络连接跟踪功能，用于跟踪网络连接的状态。</li></ul><p>这些命令通常用于配置 Linux 系统中的负载均衡和网络连接跟踪功能。在加载这些内核模块之后，就可以使用相应的工具和命令来配置和管理负载均衡和网络连接跟踪。例如，可以使用 ipvsadm 命令来配置 IP 负载均衡，使用 conntrack 命令来查看和管理网络连接跟踪表。</p><p>如果提示如下错误：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;modprobe: FATAL: Module nf_conntrack_ipv4 not found in directory /lib/modules/4.18.0-372.9.1.el8.x86_64&quot;</span><br></pre></td></tr></table></figure><p>则需要将 <code>nf_conntrack_ipv4</code> 修改为 <code>nf_conntrack</code>，然后重新执行命令，因为在高版本内核中已经把 nf_conntrack_ipv4 替换为 nf_conntrack。</p><p>nf_conntrack_ipv4 和 nf_conntrack 都是 Linux 内核中的网络连接跟踪模块，用于跟踪网络连接的状态。它们的区别在于：</p><ul><li>nf_conntrack_ipv4 模块只能跟踪 IPv4 协议的网络连接，而 nf_conntrack 模块可以跟踪 IPv4 和 IPv6 协议的网络连接。</li><li>nf_conntrack_ipv4 模块是 nf_conntrack 模块的一个子模块，它提供了 IPv4 协议的网络连接跟踪功能。因此，如果要使用 nf_conntrack_ipv4 模块，必须先加载 nf_conntrack 模块。</li></ul><p>这两个模块通常用于 Linux 系统中的网络安全和网络性能优化。它们可以被用于防火墙、负载均衡、网络流量分析等场景中，以便对网络连接进行跟踪、监控和控制。例如，可以使用 iptables 命令和 nf_conntrack 模块来实现基于连接状态的防火墙规则，或者使用 ipvsadm 命令和 nf_conntrack 模块来实现 IP 负载均衡。</p><ul><li><a href="https://www.cnblogs.com/xiangsikai/p/9525287.html">Linux 跟踪连接netfilter 调优</a></li><li><a href="https://clodfisher.github.io/2018/09/nf_conntrack/">Iptables之nf_conntrack模块</a></li></ul></li><li><p>免密登录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">为了便捷操作,在 cnode0 上创建免密登录其他节点</span></span><br><span class="line">➜ ssh-keygen -t rsa</span><br><span class="line">➜ ssh-copy-id cnode1</span><br><span class="line">➜ ssh-copy-id cnode2</span><br><span class="line">➜ ssh-copy-id cnode3</span><br></pre></td></tr></table></figure></li><li><p>创建 kubernetes 证书存放目录</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir -p /etc/kubernetes/pki</span><br></pre></td></tr></table></figure></li><li><p>重启</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ reboot</span><br></pre></td></tr></table></figure></li></ul><h3 id="下载-k8s-二进制程序">1.4 下载 k8s 二进制程序</h3><p>从官方发布地址下载二进制包 <a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.23.md">下载地址</a></p><p>下载 <a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.23.md#server-binaries">Server Binaries</a> 即可，这个包含了所有所需的二进制文件。解压后，复制二进制 <code>kube-apiserver</code>, <code>kube-scheduler</code>, <code>kube-controller-manager</code>, <code>kube-proxy</code>,<code>kubelet</code>, <code>kubectl</code> 到 master 节点 <code>/usr/local/bin</code> 目录下，复制二进制 <code>kube-proxy</code>,<code>kubelet</code> 到 worker 节点 <code>/usr/local/bin</code> 目录下。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">➜ ll /usr/local/bin/kube*</span><br><span class="line"></span><br><span class="line">-rwxr-xr-x 1 root root 128516096 Dec 29 14:59 /usr/local/bin/kube-apiserver</span><br><span class="line">-rwxr-xr-x 1 root root 118489088 Dec 29 14:59 /usr/local/bin/kube-controller-manager</span><br><span class="line">-rwxr-xr-x 1 root root  46202880 Dec 29 14:59 /usr/local/bin/kubectl</span><br><span class="line">-rwxr-xr-x 1 root root 122352824 Dec 29 14:59 /usr/local/bin/kubelet</span><br><span class="line">-rwxr-xr-x 1 root root  43581440 Dec 29 14:59 /usr/local/bin/kube-proxy</span><br><span class="line">-rwxr-xr-x 1 root root  49020928 Dec 29 14:59 /usr/local/bin/kube-scheduler</span><br></pre></td></tr></table></figure><h2 id="安装-docker">2. 安装 docker</h2><p>参考地址 <a href="https://docs.docker.com/engine/install/centos/">安装docker</a>，Docker 需要在各个节点上安装</p><ul><li><p>切换镜像源</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo</span><br></pre></td></tr></table></figure></li><li><p>查看当前镜像源中支持的 docker 版本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum list docker-ce --showduplicates</span><br></pre></td></tr></table></figure></li><li><p>安装特定版本的 docker</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install --setopt=obsoletes=0 docker-ce-20.10.14 -y</span><br></pre></td></tr></table></figure><p><code>--setopt=obsoletes=0</code> 是 yum 包管理器的一个选项，它的作用是禁用软件包依赖关系中的版本升级。</p></li><li><p>添加配置文件</p><p>Docker 在默认情况下使用的 Cgroup Driver 为 cgroupfs，而 Kubernetes 推荐使用 systemd 来替代 cgroupfs</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mkdir /etc/docker</span><br><span class="line">cat &gt; /etc/docker/daemon.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">  &quot;exec-opts&quot;: [&quot;native.cgroupdriver=systemd&quot;],</span><br><span class="line">  &quot;registry-mirrors&quot;: [&quot;https://kn0t2bca.mirror.aliyuncs.com&quot;]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure></li><li><p>启动 dokcer</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl restart docker</span><br><span class="line">systemctl enable docker</span><br></pre></td></tr></table></figure></li></ul><h2 id="创建-ca-证书">3. 创建 ca 证书</h2><h3 id="安装-cfssl">3.1 安装 cfssl</h3><p>cfssl 是一款证书签署工具，使用 cfssl 工具可以很简化证书签署过程，方便颁发自签证书。</p><p>CloudFlare's distributes <a href="https://github.com/cloudflare/cfssl">cfssl</a> source code on github page and binaries on <a href="https://pkg.cfssl.org/">cfssl website</a>.</p><p>Our documentation assumes that you will run <a href="https://github.com/cloudflare/cfssl">cfssl</a> on your local x86_64 Linux host.</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl -s -L -o /usr/local/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64</span><br><span class="line">curl -s -L -o /usr/local/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64</span><br><span class="line">chmod +x /usr/local/bin/&#123;cfssl,cfssljson&#125;</span><br></pre></td></tr></table></figure><p>离线安装的情况，直接把两个文件下载下来重命名即可</p><h3 id="创建-ca-证书-1">3.2 创建 ca 证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建的证书统一放到 /etc/kubernetes/ssl 目录，创建后复制到 /etc/kubernetes/pki 目录</span></span><br><span class="line">➜ mkdir /etc/kubernetes/ssl</span><br><span class="line"></span><br><span class="line">➜ cd /etc/kubernetes/ssl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">ca 证书创建申请</span></span><br><span class="line">➜ cat &gt; ca-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;kubernetes&quot;,</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;k8s&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;ca&quot;: &#123;</span><br><span class="line">        &quot;expiry&quot;: &quot;87600h&quot;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建 ca 证书</span></span><br><span class="line">➜ cfssl gencert -initca ca-csr.json | cfssljson -bare ca</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ll ca*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 haxi haxi 1675 Dec 30 11:32 ca-key.pem</span><br><span class="line">-rw-rw-r-- 1 haxi haxi 1314 Dec 30 11:32 ca.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 ca 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp ca*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><p>ca-csr.json 这个文件是 Kubernetes 集群中使用的证书颁发机构 (CA) 证书签名请求 (CSR) 配置文件，用于定义 CA 的证书签名请求配置。</p><p>在这个配置文件中，CN 字段指定了证书的通用名称为 kubernetes，key 字段指定了证书的密钥算法为 RSA，密钥长度为 2048 位。names 字段定义了证书的其他信息，如国家、省份、城市、组织和组织单位等。ca 字段指定了证书的过期时间为 87600 小时（即 10 年）。</p><p>这个配置文件用于创建 Kubernetes 集群中的 CA 证书，以便对集群中的其他证书进行签名和认证。</p><ul><li>CN(Common Name): kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name)</li><li>names[].O(Organization): kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group)</li></ul><p>由于这里是 CA 证书，是签发其它证书的根证书，这个证书密钥不会分发出去作为 client 证书，所有组件使用的 client 证书都是由 CA 证书签发而来，所以 CA 证书的 CN 和 O 的名称并不重要，后续其它签发出来的证书的 CN 和 O 的名称才是有用的。</p><h3 id="创建签发配置文件">3.3 创建签发配置文件</h3><p>由于各个组件都需要配置证书，并且依赖 CA 证书来签发证书，所以我们首先要生成好 CA 证书以及后续的签发配置文件。</p><p>创建用于签发其它证书的配置文件：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">证书签发配置文件</span></span><br><span class="line">➜ cat &gt; ca-config.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;signing&quot;: &#123;</span><br><span class="line">        &quot;default&quot;: &#123;</span><br><span class="line">            &quot;expiry&quot;: &quot;87600h&quot;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;profiles&quot;: &#123;</span><br><span class="line">            &quot;kubernetes&quot;: &#123;</span><br><span class="line">                &quot;usages&quot;: [</span><br><span class="line">                    &quot;signing&quot;,</span><br><span class="line">                    &quot;key encipherment&quot;,</span><br><span class="line">                    &quot;server auth&quot;,</span><br><span class="line">                    &quot;client auth&quot;</span><br><span class="line">                ],</span><br><span class="line">                &quot;expiry&quot;: &quot;87600h&quot;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>ca-config.json 这个文件是签发其它证书的配置文件，用于定义签名配置和证书配置。其中，signing 字段定义了签名配置，profiles 字段定义了不同场景下的证书配置。</p><p>在这个配置文件中，default 配置指定了默认的证书过期时间为 87600 小时（即 10 年），profiles 配置定义了一个名为 kubernetes 的证书配置，它指定了证书的用途（签名、密钥加密、服务器认证和客户端认证）和过期时间。</p><p>这个配置文件用于创建 Kubernetes 集群中的证书和密钥，以便对集群进行安全认证和加密通信。</p><ul><li>signing：定义了签名配置，包括默认的签名过期时间和各个证书配置的签名过期时间。</li><li>profiles：定义了不同场景下的证书配置，包括证书的用途、过期时间和其他属性。</li></ul><p>在使用 cfssl gencert 命令生成证书时，可以使用 <code>-config</code> 参数指定配置文件，以便根据配置文件中的规则生成符合要求的证书。如果不指定 <code>-config</code> 参数，则 cfssl gencert 命令将使用默认的配置文件。</p><h2 id="部署-etcd">4. 部署 etcd</h2><p>etcd 版本选择的是最新版本 3.5.1，下载二进制 <a href="https://github.com/etcd-io/etcd/releases/tag/v3.5.1">etcd下载链接</a></p><h3 id="颁发证书">4.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">etcd 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">hosts 字段中，IP 为所有 etcd 集群节点地址，这里可以做好规划，预留几个 IP，以备以后扩容。</span></span><br><span class="line">➜ cat &gt; etcd-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;etcd&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.20&quot;,</span><br><span class="line">        &quot;10.128.170.21&quot;,</span><br><span class="line">        &quot;10.128.170.22&quot;,</span><br><span class="line">        &quot;10.128.170.23&quot;,</span><br><span class="line">        &quot;10.128.170.24&quot;,</span><br><span class="line">        &quot;10.128.170.25&quot;,</span><br><span class="line">        &quot;cnode0&quot;,</span><br><span class="line">        &quot;cnode1&quot;,</span><br><span class="line">        &quot;cnode2&quot;,</span><br><span class="line">        &quot;cnode3&quot;,</span><br><span class="line">        &quot;cnode4&quot;,</span><br><span class="line">        &quot;cnode5&quot;,</span><br><span class="line">        &quot;cnode0.com&quot;,</span><br><span class="line">        &quot;cnode1.com&quot;,</span><br><span class="line">        &quot;cnode2.com&quot;,</span><br><span class="line">        &quot;cnode3.com&quot;,</span><br><span class="line">        &quot;cnode4.com&quot;,</span><br><span class="line">        &quot;cnode5.com&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;k8s&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 etcd 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes etcd-csr.json | cfssljson -bare etcd</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ll etcd*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 haxi haxi 1679 Dec 30 11:32 etcd-key.pem</span><br><span class="line">-rw-rw-r-- 1 haxi haxi 1440 Dec 30 11:32 etcd.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 etcd 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp etcd*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><h3 id="部署-etcd-1">4.2 部署 etcd</h3><p>下载二进制 <a href="https://github.com/etcd-io/etcd/releases/tag/v3.5.1">etcd下载链接</a>并解压，将二进制程序 <code>etcd</code> <code>etcdctl</code> 复制到 <code>/usr/local/bin</code> 目录下</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">➜ ll /usr/local/bin/etcd*</span><br><span class="line"></span><br><span class="line">-rwxrwxr-x 1 root root 21823488 Dec 29 14:13 /usr/local/bin/etcd</span><br><span class="line">-rwxrwxr-x 1 root root 16711680 Dec 29 14:13 /usr/local/bin/etcdctl</span><br></pre></td></tr></table></figure><p>编写服务配置文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir /etc/etcd</span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/etcd/etcd.conf &lt;&lt; EOF</span><br><span class="line">ETCD_NAME=&quot;etcd1&quot;</span><br><span class="line">ETCD_DATA_DIR=&quot;/var/lib/etcd/default.etcd&quot;</span><br><span class="line">ETCD_LISTEN_PEER_URLS=&quot;https://10.128.170.20:2380&quot;</span><br><span class="line">ETCD_LISTEN_CLIENT_URLS=&quot;https://10.128.170.20:2379&quot;</span><br><span class="line">ETCD_INITIAL_ADVERTISE_PEER_URLS=&quot;https://10.128.170.20:2380&quot;</span><br><span class="line">ETCD_ADVERTISE_CLIENT_URLS=&quot;https://10.128.170.20:2379&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER=&quot;etcd1=https://10.128.170.20:2380&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER_TOKEN=&quot;etcd-cluster&quot;</span><br><span class="line">ETCD_INITIAL_CLUSTER_STATE=&quot;new&quot;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">配置文件解释</span></span><br><span class="line">ETCD_NAME：节点名称，集群中唯一</span><br><span class="line">ETCD_DATA_DIR： 数据保存目录</span><br><span class="line">ETCD_LISTEN_PEER_URLS：集群内部通信监听地址</span><br><span class="line">ETCD_LISTEN_CLIENT_URLS：客户端访问监听地址</span><br><span class="line">ETCD_INITIAL_ADVERTISE_PEER_URLS：集群通告地址</span><br><span class="line">ETCD_ADVERTISE_CLIENT_URLS：客户端通告地址</span><br><span class="line">ETCD_INITIAL_CLUSTER：集群节点地址列表</span><br><span class="line">ETCD_INITIAL_CLUSTER_TOKEN：集群通信token</span><br><span class="line">ETCD_INITIAL_CLUSTER_STATE：加入集群的当前状态，new是新集群，existing表示加入已有集群</span><br></pre></td></tr></table></figure><p>编写服务启动脚本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建数据目录</span></span><br><span class="line">➜ mkdir -p /var/lib/etcd</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">创建系统服务</span></span><br><span class="line">➜ cat &gt; /lib/systemd/system/etcd.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=etcd server</span><br><span class="line">After=network.target</span><br><span class="line">After=network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=notify</span><br><span class="line">EnvironmentFile=/etc/etcd/etcd.conf</span><br><span class="line">WorkingDirectory=/var/lib/etcd</span><br><span class="line">ExecStart=/usr/local/bin/etcd \</span><br><span class="line">  --cert-file=/etc/kubernetes/pki/etcd.pem \</span><br><span class="line">  --key-file=/etc/kubernetes/pki/etcd-key.pem \</span><br><span class="line">  --trusted-ca-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --peer-cert-file=/etc/kubernetes/pki/etcd.pem \</span><br><span class="line">  --peer-key-file=/etc/kubernetes/pki/etcd-key.pem \</span><br><span class="line">  --peer-trusted-ca-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --peer-client-cert-auth \</span><br><span class="line">  --client-cert-auth</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 etcd 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now etcd</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status etcd</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u etcd</span><br></pre></td></tr></table></figure><h2 id="部署-kube-apiserver">5. 部署 kube-apiserver</h2><h3 id="颁发证书-1">5.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kube-apiserver 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">hosts 字段中，IP 为所有 kube-apiserver 节点地址，这里可以做好规划，预留几个 IP，以备以后扩容。我这里写 6 个 IP</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">10.128.170.20 10.128.170.21 10.128.170.22 10.128.170.23 10.128.170.24 10.128.170.25</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">10.96.0.1 是 service 网段的第一个 IP</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kubernetes.default.svc.cluster.local 这一串是 kube-apiserver 的 service 域名</span></span><br><span class="line">➜ cat &gt; kube-apiserver-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;kubernetes&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.20&quot;,</span><br><span class="line">        &quot;10.128.170.21&quot;,</span><br><span class="line">        &quot;10.128.170.22&quot;,</span><br><span class="line">        &quot;10.128.170.23&quot;,</span><br><span class="line">        &quot;10.128.170.24&quot;,</span><br><span class="line">        &quot;10.128.170.25&quot;,</span><br><span class="line">        &quot;cnode0&quot;,</span><br><span class="line">        &quot;cnode1&quot;,</span><br><span class="line">        &quot;cnode2&quot;,</span><br><span class="line">        &quot;cnode3&quot;,</span><br><span class="line">        &quot;cnode4&quot;,</span><br><span class="line">        &quot;cnode5&quot;,</span><br><span class="line">        &quot;cnode0.com&quot;,</span><br><span class="line">        &quot;cnode1.com&quot;,</span><br><span class="line">        &quot;cnode2.com&quot;,</span><br><span class="line">        &quot;cnode3.com&quot;,</span><br><span class="line">        &quot;cnode4.com&quot;,</span><br><span class="line">        &quot;cnode5.com&quot;,</span><br><span class="line">        &quot;10.96.0.1&quot;,</span><br><span class="line">        &quot;kubernetes&quot;,</span><br><span class="line">        &quot;kubernetes.default&quot;,</span><br><span class="line">        &quot;kubernetes.default.svc&quot;,</span><br><span class="line">        &quot;kubernetes.default.svc.cluster&quot;,</span><br><span class="line">        &quot;kubernetes.default.svc.cluster.local&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;k8s&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kube-apiserver 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-apiserver-csr.json | cfssljson -bare kube-apiserver</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ll kube-apiserver*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 haxi haxi 1675 Dec 30 11:33 kube-apiserver-key.pem</span><br><span class="line">-rw-rw-r-- 1 haxi haxi 1590 Dec 30 11:33 kube-apiserver.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 kube-apiserver 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp kube-apiserver*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><h3 id="部署-kube-apiserver-1">5.2 部署 kube-apiserver</h3><p>编写服务配置文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/kube-apiserver.conf &lt;&lt; EOF</span><br><span class="line">KUBE_APISERVER_OPTS=&quot;--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \</span><br><span class="line">  --anonymous-auth=false \</span><br><span class="line">  --bind-address=0.0.0.0 \</span><br><span class="line">  --secure-port=6443 \</span><br><span class="line">  --insecure-port=0 \</span><br><span class="line">  --authorization-mode=Node,RBAC \</span><br><span class="line">  --runtime-config=api/all=true \</span><br><span class="line">  --enable-bootstrap-token-auth \</span><br><span class="line">  --service-cluster-ip-range=10.96.0.0/16 \</span><br><span class="line">  --token-auth-file=/etc/kubernetes/token.csv \</span><br><span class="line">  --service-node-port-range=30000-32767 \</span><br><span class="line">  --tls-cert-file=/etc/kubernetes/pki/kube-apiserver.pem \</span><br><span class="line">  --tls-private-key-file=/etc/kubernetes/pki/kube-apiserver-key.pem \</span><br><span class="line">  --client-ca-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --kubelet-client-certificate=/etc/kubernetes/pki/kube-apiserver.pem \</span><br><span class="line">  --kubelet-client-key=/etc/kubernetes/pki/kube-apiserver-key.pem \</span><br><span class="line">  --service-account-key-file=/etc/kubernetes/pki/ca-key.pem \</span><br><span class="line">  --service-account-signing-key-file=/etc/kubernetes/pki/ca-key.pem \</span><br><span class="line">  --service-account-issuer=https://kubernetes.default.svc.cluster.local \</span><br><span class="line">  --etcd-cafile=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --etcd-certfile=/etc/kubernetes/pki/etcd.pem \</span><br><span class="line">  --etcd-keyfile=/etc/kubernetes/pki/etcd-key.pem \</span><br><span class="line">  --etcd-servers=https://10.128.170.21:2379 \</span><br><span class="line">  --enable-swagger-ui=true \</span><br><span class="line">  --allow-privileged=true \</span><br><span class="line">  --apiserver-count=1 \</span><br><span class="line">  --audit-log-maxage=30 \</span><br><span class="line">  --audit-log-maxbackup=3 \</span><br><span class="line">  --audit-log-maxsize=100 \</span><br><span class="line">  --audit-log-path=/var/log/kube-apiserver-audit.log \</span><br><span class="line">  --event-ttl=1h \</span><br><span class="line">  --alsologtostderr=false \</span><br><span class="line">  --log-dir=/var/log/kubernetes \</span><br><span class="line">  --v=4&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><blockquote><p>如果 etcd 是一个集群，则 <code>--etcd-servers</code> 可以添加多个，例如：<code>--etcd-servers=https://10.128.170.21:2379,https://10.128.170.22:2379,https://10.128.170.23:2379</code></p></blockquote><p>生成 token 文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/token.csv &lt;&lt; EOF</span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">(<span class="built_in">head</span> -c 16 /dev/urandom | <span class="built_in">od</span> -An -t x | <span class="built_in">tr</span> -d <span class="string">&#x27; &#x27;</span>),kubelet-bootstrap,10001,<span class="string">&quot;system:node-bootstrapper&quot;</span></span></span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><blockquote><p>在这个命令中，<code>head -c 16 /dev/urandom | od -An -t x | tr -d ' '</code> 生成了一个 16 字节的随机字符串，并将其转换为十六进制格式。这个字符串将作为令牌的值。</p><ul><li>kubelet-bootstrap 是令牌的用户名</li><li>10001 是令牌的 UID，</li><li>system:node-bootstrapper 是令牌的组名。</li></ul><p>这些值将用于 kubelet 节点的身份验证和授权。</p></blockquote><p>编写服务启动脚本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kube-apiserver.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes API Server</span><br><span class="line">Documentation=https://github.com/kubernetes/kubernetes</span><br><span class="line">After=network.target network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=notify</span><br><span class="line">EnvironmentFile=/etc/kubernetes/kube-apiserver.conf</span><br><span class="line">ExecStart=/usr/local/bin/kube-apiserver $KUBE_APISERVER_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kube-apiserver 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-apiserver</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-apiserver</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-apiserver</span><br></pre></td></tr></table></figure><h2 id="部署-kubectl">6. 部署 kubectl</h2><p>部署完 kube-apiserver 后，就可以部署 kubectl 了，因为 kubectl 可以验证 kube-apiserver 是否已经正常工作了。</p><h3 id="颁发证书-2">6.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kubectl 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">O 参数的值必须为 system:masters，因为这是 kube-apiserver 一个内置好的角色，拥有集群管理的权限</span></span><br><span class="line">➜ cat &gt; kubectl-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;clusteradmin&quot;,</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;system:masters&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kubectl 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubectl-csr.json | cfssljson -bare kubectl</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ll kubectl*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 haxi haxi 1675 Dec 30 11:34 kubectl-key.pem</span><br><span class="line">-rw-rw-r-- 1 haxi haxi 1415 Dec 30 11:34 kubectl.pem</span><br></pre></td></tr></table></figure><h3 id="生成配置文件">6.2 生成配置文件</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.20:6443 --kubeconfig=kube.config</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials clusteradmin --client-certificate=kubectl.pem --client-key=kubectl-key.pem --embed-certs=true --kubeconfig=kube.config</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context kubernetes --cluster=kubernetes --user=clusteradmin --kubeconfig=kube.config</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context kubernetes --kubeconfig=kube.config</span><br><span class="line"></span><br><span class="line">➜ mkdir -p ~/.kube</span><br><span class="line"></span><br><span class="line">➜ cp kube.config ~/.kube/config</span><br></pre></td></tr></table></figure><p>以上命令用于在本地创建一个 Kubernetes 配置文件 kube.config，并将其复制到 ~/.kube/config 文件中，以便使用 kubectl 命令与 Kubernetes 集群进行交互。</p><blockquote><p>kubectl config set-cluster 命令设置了一个名为 kubernetes 的集群，指定了以下参数：</p><ul><li><code>--certificate-authority=ca.pem</code>：指定 CA 证书文件的路径。</li><li><code>--embed-certs=true</code>：将 CA 证书嵌入到配置文件中。</li><li><code>--server=https://10.128.170.20:6443</code>：指定 API Server 的地址和端口。</li><li><code>--kubeconfig=kube.config</code>：指定要写入的配置文件路径。</li></ul><p>这些参数将用于创建一个名为 kubernetes 的集群配置，并将其写入到 kube.config 文件中。</p><p>kubectl config set-credentials 命令设置了一个名为 clusteradmin 的用户，指定了以下参数：</p><ul><li><code>--client-certificate=kubectl.pem</code>：指定客户端证书文件的路径。</li><li><code>--client-key=kubectl-key.pem</code>：指定客户端私钥文件的路径。</li><li><code>--embed-certs=true</code>：将客户端证书和私钥嵌入到配置文件中。</li><li><code>--kubeconfig=kube.config</code>：指定要写入的配置文件路径。</li></ul><p>这些参数将用于创建一个名为 clusteradmin 的用户配置，并将其写入到 kube.config 文件中。</p><p>kubectl config set-context 命令设置了一个名为 kubernetes 的上下文，指定了以下参数：</p><ul><li><code>--cluster=kubernetes</code>：指定要使用的集群。</li><li><code>--user=clusteradmin</code>：指定要使用的用户。</li><li><code>--kubeconfig=kube.config</code>：指定要写入的配置文件路径。</li></ul><p>这些参数将用于创建一个名为 kubernetes 的上下文配置，并将其写入到 kube.config 文件中。</p><p>kubectl config use-context 命令将当前上下文设置为 kubernetes，指定了以下参数：</p><ul><li><code>--kubeconfig=kube.config</code>：指定要使用的配置文件路径。</li></ul><p>这个命令将当前上下文设置为 kubernetes，以便 kubectl 命令可以使用 kube.config 文件与 Kubernetes 集群进行交互。</p></blockquote><h3 id="获取集群信息">6.3 获取集群信息</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl cluster-info</span><br><span class="line"></span><br><span class="line">Kubernetes control plane is running at https://10.128.170.20:6443</span><br><span class="line"></span><br><span class="line">To further debug and diagnose cluster problems, use &#x27;kubectl cluster-info dump&#x27;.</span><br><span class="line"></span><br><span class="line">➜ kubectl get all -A</span><br><span class="line"></span><br><span class="line">NAMESPACE   NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE</span><br><span class="line">default     service/kubernetes   ClusterIP   10.96.0.1    &lt;none&gt;        443/TCP   22m</span><br><span class="line"></span><br><span class="line">➜ kubectl get cs</span><br><span class="line"></span><br><span class="line">Warning: v1 ComponentStatus is deprecated in v1.19+</span><br><span class="line">NAME                 STATUS      MESSAGE                                                                                        ERROR</span><br><span class="line">scheduler            Unhealthy   Get &quot;https://127.0.0.1:10259/healthz&quot;: dial tcp 127.0.0.1:10259: connect: connection refused</span><br><span class="line">controller-manager   Unhealthy   Get &quot;https://127.0.0.1:10257/healthz&quot;: dial tcp 127.0.0.1:10257: connect: connection refused</span><br><span class="line">etcd-0               Healthy     &#123;&quot;health&quot;: &quot;true&quot;&#125;</span><br></pre></td></tr></table></figure><h3 id="设置-kubectl-自动补全">6.4 设置 kubectl 自动补全</h3><p>查看 kubectl 命令自动补全帮助：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">kubectl completion --help</span><br><span class="line">````</span><br><span class="line"></span><br><span class="line">安装 bash-completion：</span><br><span class="line"></span><br><span class="line">```shell</span><br><span class="line">yum install bash-completion -y</span><br></pre></td></tr></table></figure><p>设置 kubectl 自动补全配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line">echo &quot;source &lt;(kubectl completion bash)&quot; &gt;&gt; ~/.bashrc</span><br><span class="line">````</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># 7. 部署 kube-controller-manager</span></span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">## 7.1 颁发证书</span></span></span><br><span class="line"></span><br><span class="line">```shell</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kube-controller-manager 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">hosts 字段中，IP 为所有节点地址，这里可以做好规划，预留几个 IP，以备以后扩容。我这里写 6 个 IP</span></span><br><span class="line">➜ cat &gt; kube-controller-manager-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;system:kube-controller-manager&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.20&quot;,</span><br><span class="line">        &quot;10.128.170.21&quot;,</span><br><span class="line">        &quot;10.128.170.22&quot;,</span><br><span class="line">        &quot;10.128.170.23&quot;,</span><br><span class="line">        &quot;10.128.170.24&quot;,</span><br><span class="line">        &quot;10.128.170.25&quot;,</span><br><span class="line">        &quot;cnode0&quot;,</span><br><span class="line">        &quot;cnode1&quot;,</span><br><span class="line">        &quot;cnode2&quot;,</span><br><span class="line">        &quot;cnode3&quot;,</span><br><span class="line">        &quot;cnode4&quot;,</span><br><span class="line">        &quot;cnode5&quot;,</span><br><span class="line">        &quot;cnode0.com&quot;,</span><br><span class="line">        &quot;cnode1.com&quot;,</span><br><span class="line">        &quot;cnode2.com&quot;,</span><br><span class="line">        &quot;cnode3.com&quot;,</span><br><span class="line">        &quot;cnode4.com&quot;,</span><br><span class="line">        &quot;cnode5.com&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;system:kube-controller-manager&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kube-controller-manager 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ll kube-controller-manager*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 haxi haxi 1679 Dec 30 12:13 kube-controller-manager-key.pem</span><br><span class="line">-rw-rw-r-- 1 haxi haxi 1513 Dec 30 12:13 kube-controller-manager.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 kube-controler-manager 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp kube-controller-manager*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><p>system:kube-controller-manager 是 Kubernetes 中的一个预定义 RBAC 角色，用于授权 kube-controller-manager 组件对 Kubernetes API 的访问。详细介绍请参考官方文档：<a href="https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/#default-roles-and-role-bindings" class="uri">https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/#default-roles-and-role-bindings</a></p><h3 id="部署-kube-controller-manager">7.2 部署 kube-controller-manager</h3><p>编写服务配置文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/kube-controller-manager.conf &lt;&lt; EOF</span><br><span class="line">KUBE_CONTROLLER_MANAGER_OPTS=&quot;--port=0 \</span><br><span class="line">  --secure-port=10257 \</span><br><span class="line">  --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \</span><br><span class="line">  --service-cluster-ip-range=10.96.0.0/16 \</span><br><span class="line">  --cluster-name=kubernetes \</span><br><span class="line">  --cluster-signing-cert-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --cluster-signing-key-file=/etc/kubernetes/pki/ca-key.pem \</span><br><span class="line">  --cluster-signing-duration=87600h \</span><br><span class="line">  --tls-cert-file=/etc/kubernetes/pki/kube-controller-manager.pem \</span><br><span class="line">  --tls-private-key-file=/etc/kubernetes/pki/kube-controller-manager-key.pem \</span><br><span class="line">  --service-account-private-key-file=/etc/kubernetes/pki/ca-key.pem \</span><br><span class="line">  --root-ca-file=/etc/kubernetes/pki/ca.pem \</span><br><span class="line">  --leader-elect=true \</span><br><span class="line">  --controllers=*,bootstrapsigner,tokencleaner \</span><br><span class="line">  --use-service-account-credentials=true \</span><br><span class="line">  --horizontal-pod-autoscaler-sync-period=10s \</span><br><span class="line">  --alsologtostderr=true \</span><br><span class="line">  --logtostderr=false \</span><br><span class="line">  --log-dir=/var/log/kubernetes \</span><br><span class="line">  --allocate-node-cidrs=true \</span><br><span class="line">  --cluster-cidr=10.240.0.0/12 \</span><br><span class="line">  --v=4&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>生成 kubeconfig</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.20:6443 --kubeconfig=kube-controller-manager.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials kube-controller-manager --client-certificate=kube-controller-manager.pem --client-key=kube-controller-manager-key.pem --embed-certs=true --kubeconfig=kube-controller-manager.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context default --cluster=kubernetes --user=kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ cp kube-controller-manager.kubeconfig /etc/kubernetes/</span><br></pre></td></tr></table></figure><p>编写服务启动脚本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kube-controller-manager.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes controller manager</span><br><span class="line">Documentation=https://github.com/kubernetes/kubernetes</span><br><span class="line">After=network.target network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">EnvironmentFile=/etc/kubernetes/kube-controller-manager.conf</span><br><span class="line">ExecStart=/usr/local/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kube-controller-manager 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-controller-manager</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-controller-manager</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-controller-manager</span><br></pre></td></tr></table></figure><p>查看组件状态</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get cs</span><br><span class="line"></span><br><span class="line">Warning: v1 ComponentStatus is deprecated in v1.19+</span><br><span class="line">NAME                 STATUS      MESSAGE                                                                                        ERROR</span><br><span class="line">scheduler            Unhealthy   Get &quot;https://127.0.0.1:10259/healthz&quot;: dial tcp 127.0.0.1:10259: connect: connection refused</span><br><span class="line">controller-manager   Healthy     ok</span><br><span class="line">etcd-0               Healthy     &#123;&quot;health&quot;: &quot;true&quot;&#125;</span><br></pre></td></tr></table></figure><h2 id="部署-kube-scheduler">8. 部署 kube-scheduler</h2><h3 id="颁发证书-3">8.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kube-scheduler 证书签署申请</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">hosts 字段中，IP 为所有节点地址，这里可以做好规划，预留几个 IP，以备以后扩容。我这里写 6 个 IP</span></span><br><span class="line">➜ cat &gt; kube-scheduler-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;system:kube-scheduler&quot;,</span><br><span class="line">    &quot;hosts&quot;: [</span><br><span class="line">        &quot;127.0.0.1&quot;,</span><br><span class="line">        &quot;10.128.170.20&quot;,</span><br><span class="line">        &quot;10.128.170.21&quot;,</span><br><span class="line">        &quot;10.128.170.22&quot;,</span><br><span class="line">        &quot;10.128.170.23&quot;,</span><br><span class="line">        &quot;10.128.170.24&quot;,</span><br><span class="line">        &quot;10.128.170.25&quot;,</span><br><span class="line">        &quot;cnode0&quot;,</span><br><span class="line">        &quot;cnode1&quot;,</span><br><span class="line">        &quot;cnode2&quot;,</span><br><span class="line">        &quot;cnode3&quot;,</span><br><span class="line">        &quot;cnode4&quot;,</span><br><span class="line">        &quot;cnode5&quot;,</span><br><span class="line">        &quot;cnode0.com&quot;,</span><br><span class="line">        &quot;cnode1.com&quot;,</span><br><span class="line">        &quot;cnode2.com&quot;,</span><br><span class="line">        &quot;cnode3.com&quot;,</span><br><span class="line">        &quot;cnode4.com&quot;,</span><br><span class="line">        &quot;cnode5.com&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;system:kube-scheduler&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kube-scheduler 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-scheduler-csr.json | cfssljson -bare kube-scheduler</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ll kube-scheduler*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 haxi haxi 1679 Dec 30 13:19 kube-scheduler-key.pem</span><br><span class="line">-rw-rw-r-- 1 haxi haxi 1489 Dec 30 13:19 kube-scheduler.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 kube-scheduler 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp kube-scheduler*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><h3 id="部署-kube-scheduler-1">8.2 部署 kube-scheduler</h3><p>编写服务配置文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/kube-scheduler.conf &lt;&lt; EOF</span><br><span class="line">KUBE_SCHEDULER_OPTS=&quot;--address=127.0.0.1 \</span><br><span class="line">  --kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \</span><br><span class="line">  --leader-elect=true \</span><br><span class="line">  --alsologtostderr=true \</span><br><span class="line">  --logtostderr=false \</span><br><span class="line">  --log-dir=/var/log/kubernetes \</span><br><span class="line">  --v=4&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>生成 kubeconfig</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.20:6443 --kubeconfig=kube-scheduler.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials kube-scheduler --client-certificate=kube-scheduler.pem --client-key=kube-scheduler-key.pem --embed-certs=true --kubeconfig=kube-scheduler.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context default --cluster=kubernetes --user=kube-scheduler --kubeconfig=kube-scheduler.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ cp kube-scheduler.kubeconfig /etc/kubernetes/</span><br></pre></td></tr></table></figure><p>编写服务启动脚本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kube-scheduler.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes scheduler</span><br><span class="line">Documentation=https://github.com/kubernetes/kubernetes</span><br><span class="line">After=network.target network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">EnvironmentFile=/etc/kubernetes/kube-scheduler.conf</span><br><span class="line">ExecStart=/usr/local/bin/kube-scheduler $KUBE_SCHEDULER_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kube-scheduler 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-scheduler</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-scheduler</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-scheduler</span><br></pre></td></tr></table></figure><p>查看组件状态</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get cs</span><br><span class="line"></span><br><span class="line">Warning: v1 ComponentStatus is deprecated in v1.19+</span><br><span class="line">NAME                 STATUS    MESSAGE              ERROR</span><br><span class="line">controller-manager   Healthy   ok</span><br><span class="line">scheduler            Healthy   ok</span><br><span class="line">etcd-0               Healthy   &#123;&quot;health&quot;: &quot;true&quot;&#125;</span><br></pre></td></tr></table></figure><h2 id="部署-kubelet">9. 部署 kubelet</h2><p>master 节点上部署 kubelet 是可选的，一旦部署 kubelet，master 节点也可以运行 Pod，如果不希望 master 节点上运行 Pod，则可以给 master 节点打上污点。</p><p>master 节点部署 kubelet 是有好处的，一是可以通过诸如 <code>kubectl get node</code> 等命令查看节点信息，二是可以在上面部署监控系统，日志采集系统等。</p><h3 id="授权-kubelet-允许请求证书">9.1 授权 kubelet 允许请求证书</h3><p>授权 kubelet-bootstrap 用户允许请求证书</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl create clusterrolebinding kubelet-bootstrap \</span><br><span class="line">--clusterrole=system:node-bootstrapper \</span><br><span class="line">--user=kubelet-bootstrap</span><br><span class="line"></span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/kubelet-bootstrap created</span><br></pre></td></tr></table></figure><h3 id="部署-kubelet-1">9.2 部署 kubelet</h3><p>编写服务配置文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/kubelet.conf &lt;&lt; EOF</span><br><span class="line">KUBELET_OPTS=&quot;--bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.kubeconfig \</span><br><span class="line">  --config=/etc/kubernetes/kubelet.yaml \</span><br><span class="line">  --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \</span><br><span class="line">  --cert-dir=/etc/kubernetes/pki \</span><br><span class="line">  --network-plugin=cni \</span><br><span class="line">  --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6 \</span><br><span class="line">  --logtostderr=false \</span><br><span class="line">  --v=4 \</span><br><span class="line">  --log-dir=/var/log/kubernetes \</span><br><span class="line">  --fail-swap-on=false&quot;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/kubelet.yaml &lt;&lt; EOF</span><br><span class="line">kind: KubeletConfiguration</span><br><span class="line">apiVersion: kubelet.config.k8s.io/v1beta1</span><br><span class="line">address: 0.0.0.0</span><br><span class="line">port: 10250</span><br><span class="line">readOnlyPort: 0</span><br><span class="line">authentication:</span><br><span class="line">  anonymous:</span><br><span class="line">    enabled: false</span><br><span class="line">  webhook:</span><br><span class="line">    cacheTTL: 2m0s</span><br><span class="line">    enabled: true</span><br><span class="line">  x509:</span><br><span class="line">    clientCAFile: /etc/kubernetes/pki/ca.pem</span><br><span class="line">authorization:</span><br><span class="line">  mode: Webhook</span><br><span class="line">  webhook:</span><br><span class="line">    cacheAuthorizedTTL: 5m0s</span><br><span class="line">    cacheUnauthorizedTTL: 30s</span><br><span class="line">cgroupDriver: systemd</span><br><span class="line">clusterDNS:</span><br><span class="line">- 10.96.0.10</span><br><span class="line">clusterDomain: cluster.local</span><br><span class="line">healthzBindAddress: 127.0.0.1</span><br><span class="line">healthzPort: 10248</span><br><span class="line">rotateCertificates: true</span><br><span class="line">evictionHard:</span><br><span class="line">  imagefs.available: 15%</span><br><span class="line">  memory.available: 100Mi</span><br><span class="line">  nodefs.available: 10%</span><br><span class="line">  nodefs.inodesFree: 5%</span><br><span class="line">maxOpenFiles: 1000000</span><br><span class="line">maxPods: 110</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>生成 kubeconfig</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.20:6443 --kubeconfig=kubelet-bootstrap.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials kubelet-bootstrap --token=$(awk -F, &#x27;&#123;print $1&#125;&#x27; /etc/kubernetes/token.csv) --kubeconfig=kubelet-bootstrap.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context default --cluster=kubernetes --user=kubelet-bootstrap --kubeconfig=kubelet-bootstrap.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context default --kubeconfig=kubelet-bootstrap.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ cp kubelet-bootstrap.kubeconfig /etc/kubernetes/</span><br></pre></td></tr></table></figure><p>编写服务启动脚本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kubelet.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes kubelet</span><br><span class="line">After=network.target network-online.targer docker.service</span><br><span class="line">Wants=docker.service</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">EnvironmentFile=/etc/kubernetes/kubelet.conf</span><br><span class="line">ExecStart=/usr/local/bin/kubelet $KUBELET_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kubelet 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kubelet</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kubelet</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kubelet</span><br></pre></td></tr></table></figure><p>批准节点加入集群</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get csr</span><br><span class="line"></span><br><span class="line">NAME        AGE   SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION</span><br><span class="line">csr-dtprn   11s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   &lt;none&gt;              Pending</span><br><span class="line"></span><br><span class="line">➜ kubectl certificate approve csr-dtprn</span><br><span class="line"></span><br><span class="line">certificatesigningrequest.certificates.k8s.io/csr-dtprn approved</span><br><span class="line"></span><br><span class="line">➜ kubectl get csr</span><br><span class="line"></span><br><span class="line">NAME        AGE    SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION</span><br><span class="line">csr-dtprn   113s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   &lt;none&gt;              Approved,Issued</span><br></pre></td></tr></table></figure><p>查看节点</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get node</span><br><span class="line"></span><br><span class="line">NAME     STATUS     ROLES    AGE   VERSION</span><br><span class="line">cnode0   NotReady   &lt;none&gt;   57s   v1.23.6</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">此时节点状态还是 NotReady，因为还没有安装网络插件，正确安装网络插件后，状态会变为 Ready.</span></span><br></pre></td></tr></table></figure><h2 id="部署-kube-proxy">10. 部署 kube-proxy</h2><h3 id="颁发证书-4">10.1 颁发证书</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">kube-proxy 证书签署申请</span></span><br><span class="line">➜ cat &gt; kube-proxy-csr.json &lt;&lt; EOF</span><br><span class="line">&#123;</span><br><span class="line">    &quot;CN&quot;: &quot;system:kube-proxy&quot;,</span><br><span class="line">    &quot;key&quot;: &#123;</span><br><span class="line">        &quot;algo&quot;: &quot;rsa&quot;,</span><br><span class="line">        &quot;size&quot;: 2048</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;names&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;C&quot;: &quot;CN&quot;,</span><br><span class="line">            &quot;ST&quot;: &quot;GuangDong&quot;,</span><br><span class="line">            &quot;L&quot;: &quot;ShenZhen&quot;,</span><br><span class="line">            &quot;O&quot;: &quot;k8s&quot;,</span><br><span class="line">            &quot;OU&quot;: &quot;system&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">签署 kube-proxy 证书</span></span><br><span class="line">➜ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果，会生成两个证书文件</span></span><br><span class="line">➜ ll kube-proxy*pem</span><br><span class="line"></span><br><span class="line">-rw------- 1 haxi haxi 1679 Dec 31 10:26 kube-proxy-key.pem</span><br><span class="line">-rw-rw-r-- 1 haxi haxi 1407 Dec 31 10:26 kube-proxy.pem</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">复制 kube-proxy 证书到 /etc/kubernetes/pki</span></span><br><span class="line">➜ cp kube-proxy*pem /etc/kubernetes/pki</span><br></pre></td></tr></table></figure><h3 id="部署-kube-proxy-1">10.2 部署 kube-proxy</h3><p>编写服务配置文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/kube-proxy.conf &lt;&lt; EOF</span><br><span class="line">KUBE_PROXY_OPTS=&quot;--config=/etc/kubernetes/kube-proxy.yaml \</span><br><span class="line">  --logtostderr=false \</span><br><span class="line">  --v=4 \</span><br><span class="line">  --log-dir=/var/log/kubernetes&quot;</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/kube-proxy.yaml &lt;&lt; EOF</span><br><span class="line">kind: KubeProxyConfiguration</span><br><span class="line">apiVersion: kubeproxy.config.k8s.io/v1alpha1</span><br><span class="line">clientConnection:</span><br><span class="line">  kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig</span><br><span class="line">bindAddress: 0.0.0.0</span><br><span class="line">clusterCIDR: 10.240.0.0/12</span><br><span class="line">healthzBindAddress: 0.0.0.0:10256</span><br><span class="line">metricsBindAddress: 0.0.0.0:10249</span><br><span class="line">mode: ipvs</span><br><span class="line">ipvs:</span><br><span class="line">  scheduler: &quot;rr&quot;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>生成 kubeconfig</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://10.128.170.20:6443 --kubeconfig=kube-proxy.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-credentials kube-proxy --client-certificate=kube-proxy.pem --client-key=kube-proxy-key.pem --embed-certs=true --kubeconfig=kube-proxy.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config set-context default --cluster=kubernetes --user=kube-proxy --kubeconfig=kube-proxy.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig</span><br><span class="line"></span><br><span class="line">➜ cp kube-proxy.kubeconfig /etc/kubernetes/</span><br></pre></td></tr></table></figure><p>编写服务启动脚本</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /usr/lib/systemd/system/kube-proxy.service &lt;&lt; &quot;EOF&quot;</span><br><span class="line">[Unit]</span><br><span class="line">Description=Kubernetes Proxy</span><br><span class="line">Documentation=https://github.com/kubernetes/kubernetes</span><br><span class="line">After=network.target network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">EnvironmentFile=-/etc/kubernetes/kube-proxy.conf</span><br><span class="line">ExecStart=/usr/local/bin/kube-proxy $KUBE_PROXY_OPTS</span><br><span class="line">Restart=on-failure</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=65535</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>启动 kube-proxy 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-proxy</span><br></pre></td></tr></table></figure><h2 id="部署网络">11. 部署网络</h2><h3 id="部署-calico">11.1 部署 calico</h3><p>参考地址 <a href="https://projectcalico.docs.tigera.io/getting-started/kubernetes/self-managed-onprem/onpremises">calico</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line">➜ curl https://projectcalico.docs.tigera.io/v3.23/manifests/calico.yaml -O</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">修改 Pod IP 地址段，找到 CALICO_IPV4POOL_CIDR 变量，取消注释并修改如下</span></span><br><span class="line">            - name: CALICO_IPV4POOL_CIDR</span><br><span class="line">              value: &quot;10.240.0.0/12&quot;</span><br><span class="line"></span><br><span class="line">➜ kubectl apply -f calico.yaml</span><br><span class="line"></span><br><span class="line">configmap/calico-config created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/caliconodestatuses.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/ipreservations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created</span><br><span class="line">customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io/calico-node created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/calico-node created</span><br><span class="line">daemonset.apps/calico-node created</span><br><span class="line">serviceaccount/calico-node created</span><br><span class="line">deployment.apps/calico-kube-controllers created</span><br><span class="line">serviceaccount/calico-kube-controllers created</span><br><span class="line">poddisruptionbudget.policy/calico-kube-controllers created</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看网络 pod</span></span><br><span class="line">➜ kubectl get deploy,pod -n kube-system</span><br><span class="line"></span><br><span class="line">NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE</span><br><span class="line">deployment.apps/calico-kube-controllers   1/1     1            1           24m</span><br><span class="line"></span><br><span class="line">NAME                                          READY   STATUS    RESTARTS   AGE</span><br><span class="line">pod/calico-kube-controllers-6b77fff45-mxxkg   1/1     Running   0          24m</span><br><span class="line">pod/calico-node-ld4sg                         1/1     Running   0          24m</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看 node 状态</span></span><br><span class="line">➜ kubectl get node</span><br><span class="line"></span><br><span class="line">NAME     STATUS   ROLES    AGE   VERSION</span><br><span class="line">cnode0   Ready    &lt;none&gt;   38m   v1.23.6</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看 ipvs 模式</span></span><br><span class="line">➜ ipvsadm -Ln</span><br><span class="line"></span><br><span class="line">IP Virtual Server version 1.2.1 (size=4096)</span><br><span class="line">Prot LocalAddress:Port Scheduler Flags</span><br><span class="line"><span class="meta prompt_">  -&gt; </span><span class="language-bash">RemoteAddress:Port           Forward Weight ActiveConn InActConn</span></span><br><span class="line">TCP  10.96.0.1:443 rr</span><br><span class="line"><span class="meta prompt_">  -&gt; </span><span class="language-bash">10.128.170.20:6443           Masq    1      5          0</span></span><br></pre></td></tr></table></figure><p><strong>如果 node 状态仍然是 NotReady，基本上是镜像未拉取完成或拉取失败导致的，如果一段时间后仍拉取失败，则尝试手动拉取镜像。</strong></p><h3 id="授权-kube-apiserver-访问-kubelet">11.2 授权 kube-apiserver 访问 kubelet</h3><p><a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/">Using RBAC Authorization</a></p><p>应用场景：例如 kubectl exec/run/logs</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; apiserver-to-kubelet-rbac.yaml &lt;&lt; EOF</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1</span><br><span class="line">kind: ClusterRole</span><br><span class="line">metadata:</span><br><span class="line">  annotations:</span><br><span class="line">    rbac.authorization.kubernetes.io/autoupdate: &quot;true&quot;</span><br><span class="line">  labels:</span><br><span class="line">    kubernetes.io/bootstrapping: rbac-defaults</span><br><span class="line">  name: system:kube-apiserver-to-kubelet</span><br><span class="line">rules:</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - nodes/proxy</span><br><span class="line">      - nodes/stats</span><br><span class="line">      - nodes/log</span><br><span class="line">      - nodes/spec</span><br><span class="line">      - nodes/metrics</span><br><span class="line">      - pods/log</span><br><span class="line">    verbs:</span><br><span class="line">      - &quot;*&quot;</span><br><span class="line">---</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1</span><br><span class="line">kind: ClusterRoleBinding</span><br><span class="line">metadata:</span><br><span class="line">  name: system:kube-apiserver</span><br><span class="line">  namespace: &quot;&quot;</span><br><span class="line">roleRef:</span><br><span class="line">  apiGroup: rbac.authorization.k8s.io</span><br><span class="line">  kind: ClusterRole</span><br><span class="line">  name: system:kube-apiserver-to-kubelet</span><br><span class="line">subjects:</span><br><span class="line">  - apiGroup: rbac.authorization.k8s.io</span><br><span class="line">    kind: User</span><br><span class="line">    name: kubernetes</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ kubectl apply -f apiserver-to-kubelet-rbac.yaml</span><br><span class="line"></span><br><span class="line">clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created</span><br><span class="line"></span><br><span class="line">➜ kubectl logs calico-kube-controllers-6b77fff45-mxxkg -n kube-system</span><br></pre></td></tr></table></figure><h3 id="部署-coredns">11.3 部署 coredns</h3><p>参考地址 <a href="https://github.com/coredns/deployment/blob/master/kubernetes/coredns.yaml.sed">coredns</a></p><p><strong>coredns.yaml.sed 原始文件见附录章节 "16.1 coredns.yaml.sed"，该 yaml 指定使用的 coredns 的版本是 1.9.2。</strong></p><p>下载 yaml 文件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ curl https://raw.githubusercontent.com/coredns/deployment/master/kubernetes/coredns.yaml.sed -o coredns.yaml</span><br></pre></td></tr></table></figure><p>对 yaml 文件做如下修改：</p><ul><li>CLUSTER_DOMAIN 改为 cluster.local</li><li>REVERSE_CIDRS 改为 in-addr.arpa ip6.arpa</li><li>UPSTREAMNAMESERVER 改为 /etc/resolv.conf，如果报错，则改成当前网络所使用的 DNS 地址</li><li>删除 STUBDOMAINS</li><li>CLUSTER_DNS_IP 改为 10.96.0.10（应与 /etc/kubernetes/kubelet.yaml 中配置的 clusterDNS 保持一致）</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl apply -f coredns.yaml</span><br></pre></td></tr></table></figure><p>验证<strong>（如果 calico 的 pod 未就绪，请检查是否是镜像拉取未完成或镜像拉取失败）</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get deploy,pod,svc -n kube-system</span><br><span class="line">NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE</span><br><span class="line">deployment.apps/calico-kube-controllers   1/1     1            1           43m</span><br><span class="line">deployment.apps/coredns                   1/1     1            1           3m8s</span><br><span class="line"></span><br><span class="line">NAME                                          READY   STATUS    RESTARTS   AGE</span><br><span class="line">pod/calico-kube-controllers-6b77fff45-mxxkg   1/1     Running   0          43m</span><br><span class="line">pod/calico-node-ld4sg                         1/1     Running   0          43m</span><br><span class="line">pod/coredns-799bc9dbc6-qqh7h                  1/1     Running   0          3m8s</span><br><span class="line"></span><br><span class="line">NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE</span><br><span class="line">service/kube-dns   ClusterIP   10.96.0.10   &lt;none&gt;        53/UDP,53/TCP,9153/TCP   3m8s</span><br></pre></td></tr></table></figure><p>dig 测试</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">➜ yum install bind-utils -y</span><br><span class="line"></span><br><span class="line">➜ dig -t A www.baidu.com @10.96.0.10 +short</span><br><span class="line"></span><br><span class="line">www.a.shifen.com.</span><br><span class="line">182.61.200.6</span><br><span class="line">182.61.200.7</span><br></pre></td></tr></table></figure><p>pod 测试</p><p><a href="https://www.cnblogs.com/vincenshen/p/9751193.html">Kubernetes busybox nslookup问题</a></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl run -it --rm --image=busybox:1.28.3 -- sh</span><br><span class="line"></span><br><span class="line">If you don&#x27;t see a command prompt, try pressing enter.</span><br><span class="line">/ # cat /etc/resolv.conf</span><br><span class="line">nameserver 10.96.0.10</span><br><span class="line">search default.svc.cluster.local svc.cluster.local cluster.local</span><br><span class="line">options ndots:5</span><br><span class="line">/ # nslookup kubernetes.default</span><br><span class="line">Server:    10.96.0.10</span><br><span class="line">Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local</span><br><span class="line"></span><br><span class="line">Name:      kubernetes.default</span><br><span class="line">Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local</span><br><span class="line">/ # ping -c 4 www.baidu.com</span><br><span class="line">PING www.baidu.com (182.61.200.6): 56 data bytes</span><br><span class="line">64 bytes from 182.61.200.6: seq=0 ttl=52 time=6.860 ms</span><br><span class="line">64 bytes from 182.61.200.6: seq=1 ttl=52 time=6.592 ms</span><br><span class="line">64 bytes from 182.61.200.6: seq=2 ttl=52 time=6.488 ms</span><br><span class="line">64 bytes from 182.61.200.6: seq=3 ttl=52 time=7.288 ms</span><br><span class="line"></span><br><span class="line">--- www.baidu.com ping statistics ---</span><br><span class="line">4 packets transmitted, 4 packets received, 0% packet loss</span><br><span class="line">round-trip min/avg/max = 6.488/6.807/7.288 ms</span><br></pre></td></tr></table></figure><h2 id="添加-worker-节点">12. 添加 worker 节点</h2><p>worker 节点需要部署两个组件 <code>kubelet</code>, <code>kube-proxy</code>.</p><p>（master 节点执行）从 master 节点上复制以下文件到 worker 节点</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">➜ scp /etc/kubernetes/pki/ca.pem \</span><br><span class="line">       /etc/kubernetes/pki/kube-proxy.pem \</span><br><span class="line">       /etc/kubernetes/pki/kube-proxy-key.pem \</span><br><span class="line">       root@cnode1:/etc/kubernetes/pki/</span><br><span class="line"></span><br><span class="line">➜ scp /etc/kubernetes/kubelet.conf \</span><br><span class="line">       /etc/kubernetes/kubelet.yaml \</span><br><span class="line">       /etc/kubernetes/kubelet-bootstrap.kubeconfig \</span><br><span class="line">       /etc/kubernetes/kube-proxy.conf \</span><br><span class="line">       /etc/kubernetes/kube-proxy.yaml \</span><br><span class="line">       /etc/kubernetes/kube-proxy.kubeconfig \</span><br><span class="line">       root@cnode1:/etc/kubernetes/</span><br><span class="line"></span><br><span class="line">➜ scp /usr/lib/systemd/system/kubelet.service \</span><br><span class="line">       /usr/lib/systemd/system/kube-proxy.service \</span><br><span class="line">       root@cnode1:/usr/lib/systemd/system/</span><br></pre></td></tr></table></figure><p>（master 节点执行）复制 <code>kubelet</code>, <code>kube-proxy</code> 二进制程序到 /usr/local/bin</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">➜ scp /usr/local/bin/kubelet \</span><br><span class="line">       /usr/local/bin/kube-proxy \</span><br><span class="line">       root@cnode1:/usr/local/bin/</span><br></pre></td></tr></table></figure><p>（worker 节点执行）worker 节点启动 kube-proxy 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kube-proxy</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kube-proxy</span><br></pre></td></tr></table></figure><p>（worker 节点执行）worker 节点启动 kubelet 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">➜ systemctl daemon-reload</span><br><span class="line"></span><br><span class="line">➜ systemctl enable --now kubelet</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">验证结果</span></span><br><span class="line">➜ systemctl status kubelet</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看日志</span></span><br><span class="line">➜ journalctl -u kubelet</span><br></pre></td></tr></table></figure><p>（master 节点执行）批准 worker 节点加入集群</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get csr</span><br><span class="line"></span><br><span class="line">NAME        AGE   SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION</span><br><span class="line">csr-9mvtn   23s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   &lt;none&gt;              Pending</span><br><span class="line"></span><br><span class="line">➜ kubectl certificate approve csr-9mvtn</span><br><span class="line"></span><br><span class="line">certificatesigningrequest.certificates.k8s.io/csr-9mvtn approved</span><br><span class="line"></span><br><span class="line">➜ kubectl get csr</span><br><span class="line"></span><br><span class="line">NAME        AGE     SIGNERNAME                                    REQUESTOR           REQUESTEDDURATION   CONDITION</span><br><span class="line">csr-9mvtn   3m16s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   &lt;none&gt;              Approved,Issued</span><br></pre></td></tr></table></figure><p>（master 节点执行）查看节点</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl get node</span><br><span class="line"></span><br><span class="line">NAME     STATUS   ROLES    AGE     VERSION</span><br><span class="line">cnode0   Ready    &lt;none&gt;   4h31m   v1.23.6</span><br><span class="line">cnode1   Ready    &lt;none&gt;   105m    v1.23.6</span><br></pre></td></tr></table></figure><p><strong>如果 cnode1 的状态仍是 NotReady，请检查是否是镜像拉取未完成或镜像拉取失败。</strong></p><h2 id="禁止-master-节点运行-pod">13. 禁止 master 节点运行 pod</h2><p>至此 1 master 1 worker 的 k8s 二进制集群已搭建完毕。</p><p>此外，还可以给节点打上角色标签，使得查看节点信息更加直观</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">给 master 节点打上 controlplane,etcd 角色标签</span></span><br><span class="line">➜ kubectl label node cnode0 node-role.kubernetes.io/controlplane=true node-role.kubernetes.io/etcd=true</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">给 worker 节点打上 worker 角色标签</span></span><br><span class="line">➜ kubectl label node cnode1 node-role.kubernetes.io/worker=true</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看节点标签</span></span><br><span class="line">➜ kubectl get node --show-labels</span><br></pre></td></tr></table></figure><p>如果不希望 master 节点运行 Pod，则给 master 打上污点</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl taint node cnode0 node-role.kubernetes.io/controlplane=true:NoSchedule</span><br></pre></td></tr></table></figure><p>后续可以新增 2 个 etcd 节点组成 etcd 集群，新增 2 个控制平面，避免单点故障。</p><h2 id="测试应用服务部署">14. 测试应用服务部署</h2><p>创建 namespace</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl create namespace dev</span><br><span class="line"></span><br><span class="line">namespace/dev created</span><br><span class="line"></span><br><span class="line">➜ kubectl get namespace</span><br><span class="line"></span><br><span class="line">NAME              STATUS   AGE</span><br><span class="line">default           Active   15h</span><br><span class="line">dev               Active   15s</span><br><span class="line">kube-node-lease   Active   15h</span><br><span class="line">kube-public       Active   15h</span><br><span class="line">kube-system       Active   15h</span><br></pre></td></tr></table></figure><p>创建 deployment</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">➜ mkdir -p /etc/kubernetes/resources</span><br><span class="line"></span><br><span class="line">➜ cat &gt; /etc/kubernetes/resources/nginx-deployment.yaml &lt;&lt; EOF</span><br><span class="line">apiVersion: apps/v1</span><br><span class="line">kind: Deployment</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-deployment</span><br><span class="line">  namespace: dev</span><br><span class="line">spec:</span><br><span class="line">  replicas: 1</span><br><span class="line">  selector:</span><br><span class="line">    matchLabels:</span><br><span class="line">      app: nginx-pod</span><br><span class="line">  template:</span><br><span class="line">    metadata:</span><br><span class="line">      labels:</span><br><span class="line">        app: nginx-pod</span><br><span class="line">    spec:</span><br><span class="line">      containers:</span><br><span class="line">      - name: nginx</span><br><span class="line">        image: nginx:latest</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ kubectl apply -f /etc/kubernetes/resources/nginx-deployment.yaml</span><br><span class="line"></span><br><span class="line">deployment.apps/nginx-deployment created</span><br><span class="line"></span><br><span class="line">➜ kubectl get pod -n dev</span><br><span class="line"></span><br><span class="line">NAME                                READY   STATUS    RESTARTS   AGE</span><br><span class="line">nginx-deployment-7d4578b56c-cndrb   1/1     Running   0          48s</span><br></pre></td></tr></table></figure><p>创建 service</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">➜ cat &gt; /etc/kubernetes/resources/nginx-service.yaml &lt;&lt; EOF</span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: Service</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-service</span><br><span class="line">  namespace: dev</span><br><span class="line">spec:</span><br><span class="line">  selector:</span><br><span class="line">    app: nginx-pod</span><br><span class="line">  type: NodePort</span><br><span class="line">  ports:</span><br><span class="line">  - port: 80</span><br><span class="line">    targetPort: 80</span><br><span class="line">    nodePort: 30001</span><br><span class="line">EOF</span><br><span class="line"></span><br><span class="line">➜ kubectl apply -f /etc/kubernetes/resources/nginx-service.yaml</span><br><span class="line"></span><br><span class="line">service/nginx-service created</span><br><span class="line"></span><br><span class="line">➜ kubectl get svc -n dev</span><br><span class="line"></span><br><span class="line">NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE</span><br><span class="line">nginx-service   NodePort   10.96.221.226   &lt;none&gt;        80:30001/TCP   17s</span><br></pre></td></tr></table></figure><p>测试服务访问</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">➜ curl 10.128.170.20:30001 -I</span><br><span class="line"></span><br><span class="line">HTTP/1.1 200 OK</span><br><span class="line">Server: nginx/1.21.5</span><br><span class="line">Date: Sat, 21 May 2022 08:06:25 GMT</span><br><span class="line">Content-Type: text/html</span><br><span class="line">Content-Length: 615</span><br><span class="line">Last-Modified: Tue, 28 Dec 2021 15:28:38 GMT</span><br><span class="line">Connection: keep-alive</span><br><span class="line">ETag: &quot;61cb2d26-267&quot;</span><br><span class="line">Accept-Ranges: bytes</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="部署-dashboard">15. 部署 Dashboard</h2><p>在 Kubernetes 社区中，有一个很受欢迎的 Dashboard 项目，它可以给用户提供一个可视化的 Web 界面来查看当前集群的各种信息。用户可以用 Kubernetes Dashboard 部署容器化的应用、监控应用的状态、执行故障排查任务以及管理 Kubernetes 各种资源。</p><p>官方参考文档：<a href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/">https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/</a></p><p>使用 nodeport 方式将 dashboard 服务暴露在集群外，指定使用 30443 端口。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下载相关 yaml 文件</span></span><br><span class="line">➜ curl https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.0/aio/deploy/recommended.yaml -O</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">修改 Service 部分</span></span><br><span class="line">➜ vim recommended.yaml</span><br><span class="line">kind: Service</span><br><span class="line">apiVersion: v1</span><br><span class="line">metadata:</span><br><span class="line">  labels:</span><br><span class="line">    k8s-app: kubernetes-dashboard</span><br><span class="line">  name: kubernetes-dashboard</span><br><span class="line">  namespace: kubernetes-dashboard</span><br><span class="line">spec:</span><br><span class="line">  type: NodePort  # 新增</span><br><span class="line">  ports:</span><br><span class="line">    - port: 443</span><br><span class="line">      targetPort: 8443</span><br><span class="line">      nodePort: 30443  # 新增</span><br><span class="line">  selector:</span><br><span class="line">    k8s-app: kubernetes-dashboard</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">部署</span></span><br><span class="line">➜ kubectl apply -f recommended.yaml</span><br><span class="line"></span><br><span class="line">namespace/kubernetes-dashboard created</span><br><span class="line">serviceaccount/kubernetes-dashboard created</span><br><span class="line">service/kubernetes-dashboard created</span><br><span class="line">secret/kubernetes-dashboard-certs created</span><br><span class="line">secret/kubernetes-dashboard-csrf created</span><br><span class="line">secret/kubernetes-dashboard-key-holder created</span><br><span class="line">configmap/kubernetes-dashboard-settings created</span><br><span class="line">role.rbac.authorization.k8s.io/kubernetes-dashboard created</span><br><span class="line">clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created</span><br><span class="line">rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created</span><br><span class="line">deployment.apps/kubernetes-dashboard created</span><br><span class="line">service/dashboard-metrics-scraper created</span><br><span class="line">deployment.apps/dashboard-metrics-scraper created</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看 kubernetes-dashboard 下的资源</span></span><br><span class="line">➜ kubectl get deploy -n kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">NAME                        READY   UP-TO-DATE   AVAILABLE   AGE</span><br><span class="line">dashboard-metrics-scraper   1/1     1            1           12m</span><br><span class="line">kubernetes-dashboard        1/1     1            1           12m</span><br><span class="line"></span><br><span class="line">➜ kubectl get pod -n kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">NAME                                         READY   STATUS    RESTARTS   AGE</span><br><span class="line">dashboard-metrics-scraper-799d786dbf-xpvcc   1/1     Running   0          13m</span><br><span class="line">kubernetes-dashboard-546cbc58cd-hzvhr        1/1     Running   0          13m</span><br><span class="line"></span><br><span class="line">➜ kubectl get svc -n kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE</span><br><span class="line">dashboard-metrics-scraper   ClusterIP   10.96.73.62     &lt;none&gt;        8000/TCP        13m</span><br><span class="line">kubernetes-dashboard        NodePort    10.96.148.106   &lt;none&gt;        443:30443/TCP   13m</span><br></pre></td></tr></table></figure><p><strong>如果 kubernetes-dashboard 下的资源一直未就绪，请检查是否是正在拉取镜像或者镜像一直拉取失败。</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl describe pod kubernetes-dashboard-546cbc58cd-hzvhr -n kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line">Events:</span><br><span class="line">  Type    Reason     Age    From               Message</span><br><span class="line">  ----    ------     ----   ----               -------</span><br><span class="line">  Normal  Scheduled  6m20s  default-scheduler  Successfully assigned kubernetes-dashboard/kubernetes-dashboard-546cbc58cd-hzvhr to cnode1</span><br><span class="line">  Normal  Pulling    6m20s  kubelet            Pulling image &quot;kubernetesui/dashboard:v2.5.0&quot;</span><br><span class="line"></span><br><span class="line">➜ kubectl describe pod kubernetes-dashboard-546cbc58cd-hzvhr -n kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">Events:</span><br><span class="line">  Type     Reason          Age                 From               Message</span><br><span class="line">  ----     ------          ----                ----               -------</span><br><span class="line">  Normal   Scheduled       10m                 default-scheduler  Successfully assigned kubernetes-dashboard/kubernetes-dashboard-546cbc58cd-hzvhr to cnode1</span><br><span class="line">  Warning  Failed          2m1s                kubelet            Failed to pull image &quot;kubernetesui/dashboard:v2.5.0&quot;: rpc error: code = Unknown desc = dial tcp 104.18.124.25:443: i/o timeout</span><br><span class="line">  Warning  Failed          2m1s                kubelet            Error: ErrImagePull</span><br><span class="line">  Normal   SandboxChanged  2m                  kubelet            Pod sandbox changed, it will be killed and re-created.</span><br><span class="line">  Normal   BackOff         118s (x3 over 2m)   kubelet            Back-off pulling image &quot;kubernetesui/dashboard:v2.5.0&quot;</span><br><span class="line">  Warning  Failed          118s (x3 over 2m)   kubelet            Error: ImagePullBackOff</span><br><span class="line">  Normal   Pulling         106s (x2 over 10m)  kubelet            Pulling image &quot;kubernetesui/dashboard:v2.5.0&quot;</span><br><span class="line">  Normal   Pulled          25s                 kubelet            Successfully pulled image &quot;kubernetesui/dashboard:v2.5.0&quot; in 1m21.608630166s</span><br><span class="line">  Normal   Created         22s                 kubelet            Created container kubernetes-dashboard</span><br><span class="line">  Normal   Started         21s                 kubelet            Started container kubernetes-dashboard</span><br></pre></td></tr></table></figure><p>创建 service account 并绑定默认 cluster-admin 管理员集群角色</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下面创建了一个叫 admin-user 的服务账号，放在 kubernetes-dashboard 命名空间下，并将 cluster-admin 角色绑定到 admin-user 账户，这样 admin-user 账户就有了管理员的权限。</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">默认情况下，kubeadm 创建集群时已经创建了 cluster-admin 角色，我们直接绑定即可。</span></span><br><span class="line">➜ cat &gt; dashboard-admin-user.yaml &lt;&lt; EOF</span><br><span class="line">apiVersion: v1</span><br><span class="line">kind: ServiceAccount</span><br><span class="line">metadata:</span><br><span class="line">  name: admin-user</span><br><span class="line">  namespace: kubernetes-dashboard</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1</span><br><span class="line">kind: ClusterRoleBinding</span><br><span class="line">metadata:</span><br><span class="line">  name: admin-user</span><br><span class="line">roleRef:</span><br><span class="line">  apiGroup: rbac.authorization.k8s.io</span><br><span class="line">  kind: ClusterRole</span><br><span class="line">  name: cluster-admin</span><br><span class="line">subjects:</span><br><span class="line">- kind: ServiceAccount</span><br><span class="line">  name: admin-user</span><br><span class="line">  namespace: kubernetes-dashboard</span><br><span class="line">EOF</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">应用资源配置清单</span></span><br><span class="line">➜ kubectl apply -f dashboard-admin-user.yaml</span><br><span class="line"></span><br><span class="line">serviceaccount/admin-user created</span><br><span class="line">clusterrolebinding.rbac.authorization.k8s.io/admin-user created</span><br></pre></td></tr></table></figure><p>查看 admin-user 账户的 token</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">➜ kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk &#x27;&#123;print $1&#125;&#x27;)</span><br><span class="line"></span><br><span class="line">Name:         admin-user-token-vmbfm</span><br><span class="line">Namespace:    kubernetes-dashboard</span><br><span class="line">Labels:       &lt;none&gt;</span><br><span class="line">Annotations:  kubernetes.io/service-account.name: admin-user</span><br><span class="line">              kubernetes.io/service-account.uid: bc3c111d-947e-4444-8fc0-2ff69abada00</span><br><span class="line"></span><br><span class="line">Type:  kubernetes.io/service-account-token</span><br><span class="line"></span><br><span class="line">Data</span><br><span class="line">====</span><br><span class="line">ca.crt:     1367 bytes</span><br><span class="line">namespace:  20 bytes</span><br><span class="line">token:      eyJhbGciOiJSUzI1NiIsImtpZCI6Ikd6alY4Rm1QWFRiRk9VUDlta1U1QnFVM2VyUXpXSkUwRzRKek9QX2pxbUkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLXZtYmZtIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiYzNjMTExZC05NDdlLTQ0NDQtOGZjMC0yZmY2OWFiYWRhMDAiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.e-fkl4gBppWnwMy5b1PvHNf5RiBL_uAT0o_QFTy4YWwkcBSnn9JqlCBJzy6Vblw2mIekkMwuOGux-vU8V9nPuAHczsKr1Kq2leZKR0rnNrFwqge-IO-U4pkY8sfWYWPo7j5Oop1dNAKt9q33WyenjpA6T_IF-TmdtsX4AhLeZp67RrDotq80dpSjmSBzDU2rZ6gwknCfCwum_Crn1uruNiGGP4dkFifIK78RfDyCIMdMvYuwoa9hYPTFVNPZQcTRecmdtOmfXyVHpS7FfKf3YTCm9vbyqrBLyzHinYf-dBBr5ivktJKOepuqbKSoQ68Q1KnxjeG9ouWaYa3jiukArw</span><br></pre></td></tr></table></figure><p>使用输出的 token 登录 Dashboard</p><h2 id="附录">16. 附录</h2><h3 id="coredns.yaml.sed">16.1 coredns.yaml.sed</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ServiceAccount</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">rbac.authorization.k8s.io/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ClusterRole</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">kubernetes.io/bootstrapping:</span> <span class="string">rbac-defaults</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">system:coredns</span></span><br><span class="line"><span class="attr">rules:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">apiGroups:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="attr">resources:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">endpoints</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">services</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">pods</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">namespaces</span></span><br><span class="line">    <span class="attr">verbs:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">list</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">watch</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">apiGroups:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">discovery.k8s.io</span></span><br><span class="line">    <span class="attr">resources:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">endpointslices</span></span><br><span class="line">    <span class="attr">verbs:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">list</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">watch</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">rbac.authorization.k8s.io/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ClusterRoleBinding</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">annotations:</span></span><br><span class="line">    <span class="attr">rbac.authorization.kubernetes.io/autoupdate:</span> <span class="string">&quot;true&quot;</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">kubernetes.io/bootstrapping:</span> <span class="string">rbac-defaults</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">system:coredns</span></span><br><span class="line"><span class="attr">roleRef:</span></span><br><span class="line">  <span class="attr">apiGroup:</span> <span class="string">rbac.authorization.k8s.io</span></span><br><span class="line">  <span class="attr">kind:</span> <span class="string">ClusterRole</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">system:coredns</span></span><br><span class="line"><span class="attr">subjects:</span></span><br><span class="line"><span class="bullet">-</span> <span class="attr">kind:</span> <span class="string">ServiceAccount</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ConfigMap</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line"><span class="attr">data:</span></span><br><span class="line">  <span class="attr">Corefile:</span> <span class="string">|</span></span><br><span class="line"><span class="string">    .:53 &#123;</span></span><br><span class="line"><span class="string">        errors</span></span><br><span class="line"><span class="string">        health &#123;</span></span><br><span class="line"><span class="string">          lameduck 5s</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">        ready</span></span><br><span class="line"><span class="string">        kubernetes CLUSTER_DOMAIN REVERSE_CIDRS &#123;</span></span><br><span class="line"><span class="string">          fallthrough in-addr.arpa ip6.arpa</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">        prometheus :9153</span></span><br><span class="line"><span class="string">        forward . UPSTREAMNAMESERVER &#123;</span></span><br><span class="line"><span class="string">          max_concurrent 1000</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">        cache 30</span></span><br><span class="line"><span class="string">        loop</span></span><br><span class="line"><span class="string">        reload</span></span><br><span class="line"><span class="string">        loadbalance</span></span><br><span class="line"><span class="string">    &#125;STUBDOMAINS</span></span><br><span class="line"><span class="string"></span><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">apps/v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Deployment</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">    <span class="attr">kubernetes.io/name:</span> <span class="string">&quot;CoreDNS&quot;</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="comment"># replicas: not specified here:</span></span><br><span class="line">  <span class="comment"># 1. Default is 1.</span></span><br><span class="line">  <span class="comment"># 2. Will be tuned in real time if DNS horizontal auto-scaling is turned on.</span></span><br><span class="line">  <span class="attr">strategy:</span></span><br><span class="line">    <span class="attr">type:</span> <span class="string">RollingUpdate</span></span><br><span class="line">    <span class="attr">rollingUpdate:</span></span><br><span class="line">      <span class="attr">maxUnavailable:</span> <span class="number">1</span></span><br><span class="line">  <span class="attr">selector:</span></span><br><span class="line">    <span class="attr">matchLabels:</span></span><br><span class="line">      <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">  <span class="attr">template:</span></span><br><span class="line">    <span class="attr">metadata:</span></span><br><span class="line">      <span class="attr">labels:</span></span><br><span class="line">        <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">    <span class="attr">spec:</span></span><br><span class="line">      <span class="attr">priorityClassName:</span> <span class="string">system-cluster-critical</span></span><br><span class="line">      <span class="attr">serviceAccountName:</span> <span class="string">coredns</span></span><br><span class="line">      <span class="attr">tolerations:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">key:</span> <span class="string">&quot;CriticalAddonsOnly&quot;</span></span><br><span class="line">          <span class="attr">operator:</span> <span class="string">&quot;Exists&quot;</span></span><br><span class="line">      <span class="attr">nodeSelector:</span></span><br><span class="line">        <span class="attr">kubernetes.io/os:</span> <span class="string">linux</span></span><br><span class="line">      <span class="attr">affinity:</span></span><br><span class="line">         <span class="attr">podAntiAffinity:</span></span><br><span class="line">           <span class="attr">requiredDuringSchedulingIgnoredDuringExecution:</span></span><br><span class="line">           <span class="bullet">-</span> <span class="attr">labelSelector:</span></span><br><span class="line">               <span class="attr">matchExpressions:</span></span><br><span class="line">               <span class="bullet">-</span> <span class="attr">key:</span> <span class="string">k8s-app</span></span><br><span class="line">                 <span class="attr">operator:</span> <span class="string">In</span></span><br><span class="line">                 <span class="attr">values:</span> [<span class="string">&quot;kube-dns&quot;</span>]</span><br><span class="line">             <span class="attr">topologyKey:</span> <span class="string">kubernetes.io/hostname</span></span><br><span class="line">      <span class="attr">containers:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">        <span class="attr">image:</span> <span class="string">coredns/coredns:1.9.2</span></span><br><span class="line">        <span class="attr">imagePullPolicy:</span> <span class="string">IfNotPresent</span></span><br><span class="line">        <span class="attr">resources:</span></span><br><span class="line">          <span class="attr">limits:</span></span><br><span class="line">            <span class="attr">memory:</span> <span class="string">170Mi</span></span><br><span class="line">          <span class="attr">requests:</span></span><br><span class="line">            <span class="attr">cpu:</span> <span class="string">100m</span></span><br><span class="line">            <span class="attr">memory:</span> <span class="string">70Mi</span></span><br><span class="line">        <span class="attr">args:</span> [ <span class="string">&quot;-conf&quot;</span>, <span class="string">&quot;/etc/coredns/Corefile&quot;</span> ]</span><br><span class="line">        <span class="attr">volumeMounts:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">config-volume</span></span><br><span class="line">          <span class="attr">mountPath:</span> <span class="string">/etc/coredns</span></span><br><span class="line">          <span class="attr">readOnly:</span> <span class="literal">true</span></span><br><span class="line">        <span class="attr">ports:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">containerPort:</span> <span class="number">53</span></span><br><span class="line">          <span class="attr">name:</span> <span class="string">dns</span></span><br><span class="line">          <span class="attr">protocol:</span> <span class="string">UDP</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">containerPort:</span> <span class="number">53</span></span><br><span class="line">          <span class="attr">name:</span> <span class="string">dns-tcp</span></span><br><span class="line">          <span class="attr">protocol:</span> <span class="string">TCP</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">containerPort:</span> <span class="number">9153</span></span><br><span class="line">          <span class="attr">name:</span> <span class="string">metrics</span></span><br><span class="line">          <span class="attr">protocol:</span> <span class="string">TCP</span></span><br><span class="line">        <span class="attr">securityContext:</span></span><br><span class="line">          <span class="attr">allowPrivilegeEscalation:</span> <span class="literal">false</span></span><br><span class="line">          <span class="attr">capabilities:</span></span><br><span class="line">            <span class="attr">add:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">NET_BIND_SERVICE</span></span><br><span class="line">            <span class="attr">drop:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="string">all</span></span><br><span class="line">          <span class="attr">readOnlyRootFilesystem:</span> <span class="literal">true</span></span><br><span class="line">        <span class="attr">livenessProbe:</span></span><br><span class="line">          <span class="attr">httpGet:</span></span><br><span class="line">            <span class="attr">path:</span> <span class="string">/health</span></span><br><span class="line">            <span class="attr">port:</span> <span class="number">8080</span></span><br><span class="line">            <span class="attr">scheme:</span> <span class="string">HTTP</span></span><br><span class="line">          <span class="attr">initialDelaySeconds:</span> <span class="number">60</span></span><br><span class="line">          <span class="attr">timeoutSeconds:</span> <span class="number">5</span></span><br><span class="line">          <span class="attr">successThreshold:</span> <span class="number">1</span></span><br><span class="line">          <span class="attr">failureThreshold:</span> <span class="number">5</span></span><br><span class="line">        <span class="attr">readinessProbe:</span></span><br><span class="line">          <span class="attr">httpGet:</span></span><br><span class="line">            <span class="attr">path:</span> <span class="string">/ready</span></span><br><span class="line">            <span class="attr">port:</span> <span class="number">8181</span></span><br><span class="line">            <span class="attr">scheme:</span> <span class="string">HTTP</span></span><br><span class="line">      <span class="attr">dnsPolicy:</span> <span class="string">Default</span></span><br><span class="line">      <span class="attr">volumes:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">config-volume</span></span><br><span class="line">          <span class="attr">configMap:</span></span><br><span class="line">            <span class="attr">name:</span> <span class="string">coredns</span></span><br><span class="line">            <span class="attr">items:</span></span><br><span class="line">            <span class="bullet">-</span> <span class="attr">key:</span> <span class="string">Corefile</span></span><br><span class="line">              <span class="attr">path:</span> <span class="string">Corefile</span></span><br><span class="line"><span class="meta">---</span></span><br><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Service</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line">  <span class="attr">name:</span> <span class="string">kube-dns</span></span><br><span class="line">  <span class="attr">namespace:</span> <span class="string">kube-system</span></span><br><span class="line">  <span class="attr">annotations:</span></span><br><span class="line">    <span class="attr">prometheus.io/port:</span> <span class="string">&quot;9153&quot;</span></span><br><span class="line">    <span class="attr">prometheus.io/scrape:</span> <span class="string">&quot;true&quot;</span></span><br><span class="line">  <span class="attr">labels:</span></span><br><span class="line">    <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">    <span class="attr">kubernetes.io/cluster-service:</span> <span class="string">&quot;true&quot;</span></span><br><span class="line">    <span class="attr">kubernetes.io/name:</span> <span class="string">&quot;CoreDNS&quot;</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line">  <span class="attr">selector:</span></span><br><span class="line">    <span class="attr">k8s-app:</span> <span class="string">kube-dns</span></span><br><span class="line">  <span class="attr">clusterIP:</span> <span class="string">CLUSTER_DNS_IP</span></span><br><span class="line">  <span class="attr">ports:</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">dns</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">53</span></span><br><span class="line">    <span class="attr">protocol:</span> <span class="string">UDP</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">dns-tcp</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">53</span></span><br><span class="line">    <span class="attr">protocol:</span> <span class="string">TCP</span></span><br><span class="line">  <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">metrics</span></span><br><span class="line">    <span class="attr">port:</span> <span class="number">9153</span></span><br><span class="line">    <span class="attr">protocol:</span> <span class="string">TCP</span></span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://www.haxi.cc/archives/setup-k8s-1-23-1-cluster-using-binary.html">二进制部署 K8s 集群 1.23.1 版本</a></p><p><a href="https://hebye.com/docs/k8s/k8s-1ct8ioki6h2qk#2nop09">部署一套完整的企业级K8s集群</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;通过本文的指导，读者可以了解如何通过二进制的方式部署 Kubernetes 1.23.6 版本集群。二进制部署可以加深对 Kubernetes 各组件的理解，可以灵活地将各个组件部署到不同的机器，以满足自身的要求。但是需要注意的是，二进制部署需要手动配置各个组件，需要一定的技术水平和经验。&lt;/p&gt;</summary>
    
    
    
    <category term="cloud" scheme="https://www.wylu.me/categories/cloud/"/>
    
    <category term="kubernetes" scheme="https://www.wylu.me/categories/cloud/kubernetes/"/>
    
    
    <category term="kubernetes" scheme="https://www.wylu.me/tags/kubernetes/"/>
    
  </entry>
  
  <entry>
    <title>使用GNU-make管理项目</title>
    <link href="https://www.wylu.me/posts/cec71891/"/>
    <id>https://www.wylu.me/posts/cec71891/</id>
    <published>2020-10-25T09:40:46.000Z</published>
    <updated>2022-11-23T17:04:10.543Z</updated>
    
    <content type="html"><![CDATA[<p>在本文中读者会看到有关 make 的介绍，make 是一种控制编译或者重复编译软件的工具。make 可以自动管理软件编译的内容、方式和时机，从而使程序员能够把精力集中在编写代码上。</p><span id="more"></span><h1 id="使用-gnu-make-管理项目">使用 GNU make 管理项目</h1><div class="note info"><p><strong>第 4 章 使用 GNU make 管理项目</strong> 原文出处：《GNU/Linux编程指南》第二版 原文作者： Kurt Wall 等著</p></div><h2 id="为何使用-make">1 为何使用 make</h2><p>除了最简单的软件项目，make 对于其他所有项目而言都很必要。首先，包含多个源代码文件的项月在编译时都有长而且复杂的命令行。而且，编程项目经常需要使用那些很少用到且难以记忆的特殊编译选项。make 可以通过把这些复杂而难记的命令行保存在 makefle 文件中来解决上述两个问题，makefile 将在下一小节讨论。</p><p>make 还能减少重复编译所需要的时间，因为它很聪明，能够判断哪些文件被修改过。进而只重新编译程序被修改过的部分。makefile 为项目构建了一个依赖信息数据库，因而可以让 make 在每次编译前检查是否可以找到所有需要的文件。make 还可以让你建立一个稳定的编译环境。最后，make 可以让编译过程自动执行，因为从 shell 脚本或者 cron (定时)作业调用 make 非常容易。</p><h2 id="编写-makefile">2 编写 makefile</h2><p>make 是怎样完成这些神奇工作的呢？是通过使用 makefile 文件做到的。</p><p>makefile 是一个文本形式的数据库文件，其中包含一些规则告诉 make 编译哪些文件、怎样编译以及在什么条件下去编译。每条规则包含以下内容：</p><ul><li>一个 "目标体" (target)，即 make 最终需要创建的东西。</li><li>包含一个或多个 "依赖体" (dependency)的列表，依赖体通常是编泽目标体需要 的其他文件。</li><li>为了从指定的依赖体创建出目标体所需执行的 "命令" (command) 的列表。</li></ul><p>虽然目标体通常都是程序，但它们可以是诸如文本文件、手册页面等任何东西。目标体甚至能测试和设置环境变量。类似地，也可以定义依赖体以确保编译开始前存在某个特殊的环境变量。最后，makefile 中的命令可以是编译器的命令或 shell 命令，它们能设置环境变量、删除文件,或者任何俞令行所能完成的功能,如从 FTP 站点下载文件等。GNU make 被调用后会顺序查找名为 GNUmakefile、makefile 或 Makefile 的文件。出于某种原因，可能只是习惯和长期形成的约定吧，大多数 Linux 程序员使用最后一种形式 Makefile。</p><p>Makefile 规则有下列通用形式：</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">target: dependency [dependency [...]</span></span><br><span class="line">command</span><br><span class="line">command</span><br><span class="line">[...]</span><br></pre></td></tr></table></figure><p><strong>警告:</strong></p><p><strong>每一个命令的第一个字符必须是制表符</strong>，仗使用 8 个空格是不够的。这一点经常不被人们注意， 并且当所使用的编辑器友好的将制表符转换成 8 个空格时，会产生问题；因为如果用空格代替制表符，make 会在执行过程中显示 Missing Separator (缺少分隔符)并停止。</p><p>target 是需要创建的二进制文件或目标文件。dependency 是在创建 target 时需要输入的一个或多个文件的列表。命令序列是创建 target 文件所需要的步骤，如编译命令。此外，除非特别指定，否则 make 的工作目录就是当前目录。</p><h2 id="编写-makefile-的规则">3 编写 makefile 的规则</h2><p>如果上一节的内容对你来说太抽象， 那么本节使用程序清单 4.1 再具体讨论。这是用于编译第 3 章中出现的程序 howdy 和 hello 的 makefile 文件。</p><p>程序清单 4.1 演示目标体、依赖体和命令的简单 makefile 文件</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">howdy: howdy.o helper.o helper.h</span></span><br><span class="line">gcc howdy.o helper.o -o howdy</span><br><span class="line"></span><br><span class="line"><span class="section">helper.o: helper.c helper.h</span></span><br><span class="line">gcc -c helper.c</span><br><span class="line"></span><br><span class="line"><span class="section">howdy.o: howdy.c</span></span><br><span class="line">gcc -c howdy.c</span><br><span class="line"></span><br><span class="line"><span class="section">hello: hello.c</span></span><br><span class="line">gcc hello.c -o hello</span><br><span class="line"></span><br><span class="line"><span class="section">all: howdy hello</span></span><br><span class="line"></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">rm howdy hello *.o</span><br></pre></td></tr></table></figure><p>要编译 howdy，只需在 makefile 所在目录下输入 make 即可。就这么简单。</p><p>这个 makefile 文件包含 6 条规则。第一个目标体 howdy 称为默认(default) 目标体，这是 make 要创建的文件。howdy 有 3 个依赖体，分别为 howdy.o、helper.o 和 helper.h，要编译生成 howdy，必须要有这 3 个文件。第二行调用编译器的命令供 make 执行来创建 howdy。由对第 3 章内容的回忆可知，这条命令从两个目标文件创建名为 howdy 的可执行文件。把头文件 helper.h 作为一个依赖体列入是为了避免编译器调用未声明的函数产生出错信息。接下来的两条规则告诉 make 怎样生成单个目标文件，helper.o 和 howdy.o。这些规则使用了 gcc 的 <code>-c</code> 选项，只创建目标文件但跳过链接。如果只想生成两个目标文件而不生成 howdy 本身，可以使用下面两条命令:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">make helper.o</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">make howdy.o</span></span><br></pre></td></tr></table></figure><p>更简洁一点，只需使用</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">make helper.o howdy.o</span></span><br></pre></td></tr></table></figure><p>正如你所看到的那样，make 允许把多个目标作为参数。这两种方法都能使用相应的规则和命令生成目标文件。图 4.1 给出了这个过程的图示。</p><p>图4.1 把生成 howdy 的步骤归结到第 3 章讨论的一般性的 预处理/编译/链接 过程上。howdy.c 和 helper.c 这两个源代码文件经预处理后编译成目标文件。然后链接器把来自文件 howdy.o 和 helper.o 的目标代码和标准库以及 C 启动代码链接到一起生成二进制文件 hello。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/9fdf0734b6ed1ac256008456d12d2187.png" alt="compile" /></p><p>现在，make 的价值就体现出来了：通常情况下，如果试图在依赖体 helper.o 和 howdy.o 不存在的情况下使用所示的命令编译 howdy，则 gcc 会报错井退出。另一方面，在看到 howdy 需要这两个文件（和 helper.c）后，make 先确认它们是否存在，如果不存在则首先执行命令生成它们，然后再返回到第一条规则创建可执行文件 howdy。当然，如果 helper.h 不存在，make 也会放弃执行，因为它没有创建 helper.h 的规则。</p><p>"一切都很好"，也许你会这么想，"但是 make 怎样知道什么时候需要重新编译一个文件呢？" 答案极其简单：如果指定的目标文件 make 找不到，make 就会生成它。如果目标体存在，make 会对目标体文件和依赖体文件的时间戳进行比较。如果有一个或多个依赖体比目标体新，make 就重新编译生成目标体，因为 make 认为新的依赖体意味着对代码做过修改，必须把改动融入到目标体中去。</p><p>第四条规则相当简单。它定义了如何编译生成第 3 章介绍的简单程序 hello。第五条是创建 hello 和 howdy 的笼统规则，它还表明甚至二进制文件都能作依赖体。下一小节将讨论第六条规则，即伪目标。</p><h3 id="伪目标">3.1 伪目标</h3><p>除了一般的文件目标体， 比如 howdy 和 hello 之外，make 也允许指定伪目标。称其为伪目标是因为它们并不对应于实际的文件。程序清单 4.1 中最后一个目标体 clean 就是伪目标。伪目标体规定了 make 应该执行的命令。但是，因为 clean 没有依赖体，所以它的命令不会被自动执行。下面解释 make 是如何工作的：当遇到目标体 clean 时，make 先查看其是否有依赖体，因为 clean 没有依赖体，所以 make 认为目标体是最新的而不执行任何操作。为了编译这个目标体，必须输入make clean。在本例中，clean 删除了可执行文件 hello 和 howdy 以及构成 howdy 的目标文件。在创建和发行仅包含源代码的压缩包或者需要彻底重新编译时可能会用到这样一个目标体。</p><p>然而，如果恰巧有一个名为 clean 的文件存在，make 就会发现它。然后和前面一样，因为 clean 没有依赖体文件，make 就认为这个文件是最新的而不会执行相关命令。为了处理这类情况，需要使用特殊的 make 目标体 <code>.PH0NY</code>。 <code>.PHONY</code> 的依赖体文件的含义和通常一样， 但是 make 不检查是否存在有文件名和依赖体中的一个名字相匹配的文件，而是直接执行与之相关的命令。在使用了 <code>.PHONY</code> 之后，前面的例子如下：</p><p>程序清单 4.2 带有 PHONY 目标的 Makefile 文件</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">howdy: howdy.o helper.o helper.h</span></span><br><span class="line">gcc howdy.o helper.o -o howdy</span><br><span class="line"></span><br><span class="line"><span class="section">helper.o: helper.c helper.h</span></span><br><span class="line">gcc -c helper.c</span><br><span class="line"></span><br><span class="line"><span class="section">howdy.o: howdy.c</span></span><br><span class="line">gcc -c howdy.c</span><br><span class="line"></span><br><span class="line"><span class="section">hello: hello.c</span></span><br><span class="line">gcc hello.c -o hello</span><br><span class="line"></span><br><span class="line"><span class="section">all: howdy hello</span></span><br><span class="line"></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: clean</span></span><br><span class="line"></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">rm howdy hello *.o</span><br></pre></td></tr></table></figure><h3 id="变量">3.2 变量</h3><p>为了简化编辑和维护 makefile，make 允许在 makefile 中创建和使用变量。所谓的变量其实是用指定文本串在 makefile 中定义的一个名字，这个文本串就是变量的值。下面是定义变量的一般方法:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">VARNAME=some_text [...]</span><br></pre></td></tr></table></figure><p>把变量用括号括起来，井在前面加上 "$" 符号，就可以引用变量的值：</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$(VARNAME)</span></span><br></pre></td></tr></table></figure><p>此时，VARNAME 在等式右端展开为它所代表的文本。变量一般都在 makefle 的头部定义，并且，按照惯例，所有的 makefle 变量都应该是大写( 虽然这不是必须的)。这样，如果变量的值发生变化，就只需要在一个地方修改，从而简化了 makefile 的维护。现在，继续现在修改程序清单 4.1，加入两个变量，结果如程序清单 4.3 所示。</p><p>程序清单 4.3 在 makefle 中使用变量</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">OBJS = howdy.o helper.o</span><br><span class="line">HDRS = helper.h</span><br><span class="line"></span><br><span class="line"><span class="section">howdy: <span class="variable">$(OBJS)</span> <span class="variable">$(HDRS)</span></span></span><br><span class="line">gcc <span class="variable">$(OBJS)</span> -o howdy</span><br><span class="line"></span><br><span class="line"><span class="section">helper.o: helper.c <span class="variable">$(HDRS)</span></span></span><br><span class="line">gcc -c helper.c</span><br><span class="line"></span><br><span class="line"><span class="section">howdy.o: howdy.c</span></span><br><span class="line">gcc -c howdy.c</span><br><span class="line"></span><br><span class="line"><span class="section">hello: hello.c</span></span><br><span class="line">gcc hello.c -o hello</span><br><span class="line"></span><br><span class="line"><span class="section">all: howdy hello</span></span><br><span class="line"></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">rm howdy hello *.o</span><br></pre></td></tr></table></figure><p>OBJS 和 HDRS 在被引用的每个地方都展开成它的取值。编译时也是如此。</p><p>实际上，make 使用两种变量：<strong>递归展开变量和简单展开变量</strong>。递归展开变量在引用时逐层展开，即如果在展开式中包含了对其他变量的引用，则这些变量也将被展开，直到没有需要展开的变量为止，这就是所谓的递归展开。下面的例子有助于弄清这个概念。</p><p>假设变量 TOPDIR 和 SRCDIR 如下定义:</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">TOPDIR = /home/kwall/myproject</span><br><span class="line">SRCDIR = <span class="variable">$(TOPDIR)</span>/src</span><br></pre></td></tr></table></figure><p>这样，SRCDIR 的值是 /home/kwall/myproject/src，则工作正常。但是，考虑下面的变量定义:</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CC = gcc</span><br><span class="line">CC = <span class="variable">$(CC)</span> -o</span><br></pre></td></tr></table></figure><p>很清楚，定义者想要得到的结果是 "CC=gcc -o"。但是实际并非如此；CC 在被引用时递归展开，从而陷入一个无限循环中；CC 将扩展为 <code>$(CC)</code> 的值，从而永远也读不到 <code>-o</code> 选项。幸运的是，make 能够检测到这个问题并报告错误：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*** Recursive variable &#x27;CC&#x27; references itself (eventually). Stop</span><br></pre></td></tr></table></figure><p>为了避免这个问题，可以使用<strong>简单展开变量</strong>。与<strong>递归展开变量</strong>在引用时展开不同，简单展开变量在定义处展开，并且只展开一次，从而消除了变量的嵌套引用。在定义时,其语法与递归展开变量有细微的不同：</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CC := gcc -o</span><br><span class="line">CC += -O2</span><br></pre></td></tr></table></figure><p>第一个定义使用 <code>:=</code> 设置 CC 的值为 <code>gcc -o</code>，第二个定义使用 "+=" 在前面定义的 CC 后附加了 <code>-O2</code>，从而 CC 最终的值是 <code>gcc -o -O2</code>。如果在使用 make 变量时遇到 "VARNAME references itself" 这类错误信息，就可以使用简单展开变量来解决。一些程序员仅使用简单展开变量，以避免出现意想不到的问题；但既然现在是在 Linux 上，你可以自由选择使用的方式。</p><p>除用户定义变量外, make 也允许使用环境变量、自动变量和预定义变量。使用环境变量非常简单。在启动时，make 读取己定义的环境变量，并且创建与之同名同值的变量。但是，如果 makefile 中有同名的变量，则这个变量将取代与之相应的环境变量，所以应当注意这一点。</p><p>此外，make 也提供一长串预定义变量和自动变量，但是它们看起来有些神秘。之所以称为自动变量是因为 make 自动用特定的、熟知的值替换它们。表 4.1 给出了部分自动变量。</p><h4 id="表-4.1-自动变量">表 4.1 自动变量</h4><table><thead><tr class="header"><th>变量</th><th>说明</th></tr></thead><tbody><tr class="odd"><td>$@</td><td>规则的目标所对应的文件名</td></tr><tr class="even"><td>$&lt;</td><td>规则中的第一个相关文件名</td></tr><tr class="odd"><td>$^</td><td>规则中所有相关文件的列表，以空格为分隔符</td></tr><tr class="even"><td>$?</td><td>规则中日期新于目标的所有相关文件的列表，以空格为分隔符</td></tr><tr class="odd"><td>$(<span class="citation" data-cites="D">@D</span>)</td><td>目标文件的目录部分(如果目标在子目录中)</td></tr><tr class="even"><td>$(<span class="citation" data-cites="F">@F</span>)</td><td>目标文件的文件名部分(如果目标在子目录中)</td></tr></tbody></table><p>除了表 4.1 列出的自动变量外，make 还预定义了许多其他变量，用于定义程序名或给这些程序传递标志和参数。这些预定义的变量看上去更像常规的 make 变量而不是像字符名称的自动变量。表 4.2 给出了一些有用的预定义变量。</p><h4 id="表4.2-用于程序名和标志的预定义变量">表4.2 用于程序名和标志的预定义变量</h4><table><thead><tr class="header"><th>变量</th><th>说明</th></tr></thead><tbody><tr class="odd"><td>AR</td><td>归档维护程序，默认值=ar</td></tr><tr class="even"><td>AS</td><td>汇编程序，默认值=as</td></tr><tr class="odd"><td>CC</td><td>C 编译程序，默认值=cc</td></tr><tr class="even"><td>CPP</td><td>C 预处理程序，默认值=cpp</td></tr><tr class="odd"><td>RM</td><td>文件删除程序，默认值="rm -f"</td></tr><tr class="even"><td>ARFLAGS</td><td>传给归档维护程序的标志，默认值=rv</td></tr><tr class="odd"><td>ASFLAGS</td><td>传给汇编程序的标志，没有默认值</td></tr><tr class="even"><td>CFLAGS</td><td>传给 C 编译器的标志，没有默认值</td></tr><tr class="odd"><td>CPPFLAGS</td><td>传给 C 预处理程序的标志，没有默认值</td></tr><tr class="even"><td>LDFLAGS</td><td>传给链接程序（Id）的标志，没有默认值</td></tr></tbody></table><p>如果需要，可以在 makefile 中重新定义这些变量，但是在大多数情况下，这些默认值都是合理的。</p><h3 id="隐式规则">3.3 隐式规则</h3><p>除了在 makefile 文件中显式指定的规则（称为显式规则）外，make 还有一整套隐式规则，或称为预定义规则。这些规则多数有特殊目的而且用途有限，所以在这里只介绍几种最常用的隐式规则。隐式规则简化了 makefile 的编写和维护。</p><p>假设有下面这样的一个 makefile：</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">OBJS = editor.o screen.o keyboard.o</span><br><span class="line"></span><br><span class="line"><span class="section">editor: <span class="variable">$(OBJS)</span></span></span><br><span class="line">cc -o editor <span class="variable">$(OBJS)</span></span><br><span class="line"></span><br><span class="line"><span class="meta"><span class="keyword">.PHONY</span>: clean</span></span><br><span class="line"></span><br><span class="line"><span class="section">clean:</span></span><br><span class="line">rm editor <span class="variable">$(OBJS)</span></span><br></pre></td></tr></table></figure><p>默认目标 editor 所对应的命令提及了 editor.o，screen.o 和 keyboard.o，但是 makefile 中没有怎样编译生成这些目标的规则。此时，make 就使用所谓的隐式规则，实际上，对每一个名为 somefile.o 的目标（object）文件，make 首先找到与之相应的源代码 somefile.c，并且用 <code>gcc -c somefile.c -o somefile.o</code> 编译生成这个目标文件。所以，在本例中 make 先查找名 为 editor.c，screen.c 和 keyboard.c 的文件并将它们编译为目标文件（editor.o，screen.o 和 keyboard.o），然后，编译生成默认目标 editor。</p><p>实际的机制比这里所描述的要全面。目标文件（.o） 可以从 C、Pascal 和 Fortran 等源代码中生成，所以 make 也应去查找符合实际情况的相关文件。所以，如果在工作目录下有 editor.p，screen.p 和 keyboard.p 三个 Pascal 文件（p 通常被认为是 Pascal 源代码的扩展名），make 就会激活 Pascal 编译器来编译它们，而不用 C 编译器。因此，如果出于某种原因而在项目中需要使用多种语言时，就不能依靠隐式规则，因为此时使用该规则所得到的结果可能会与期望的有所不同。</p><h3 id="模式规则">3.4 模式规则</h3><p>通过定义用户自己的隐式规则，模式规则提供了扩展 make 的隐式规则的一种方法。模式规则类似于普通规则，但是它的目标必定含有符号 "%"，这个符号可以与任何非空字符串匹配；为与目标中的 "%" 匹配，这个规则的相关文件部分也必须使用 "%"。例如，下面的规则：</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">%.o: %.c</span></span><br></pre></td></tr></table></figure><p>告诉 make 所有形为 somename.o 的目标（object）文件都应从源文件 somename.c 编译而来。</p><p>与隐式规则一样， make 预定义了一些模式规则：</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">%.o: %.c</span></span><br><span class="line"><span class="variable">$(CC)</span> -c <span class="variable">$(CFLAGS)</span> <span class="variable">$(CPPFLAGS)</span> <span class="variable">$&lt;</span> -o <span class="variable">$@</span></span><br></pre></td></tr></table></figure><p>与前面的例子相同，make 定义了一条规则，即任何 x.o 的文件都从 x.c 编译而来。每次使用该规则时，该规则用自动变量 <code>$&lt;</code> 和 <code>$@</code> 来代替第一个依赖体和目标体。此外，变量 <code>$(CC)</code>，<code>$(CFLAGS)</code> 和 <code>$(CPPFLAGS)</code> 的默认值如表 4.2 所示。</p><h3 id="注释">3.5 注释</h3><p>在 makefile 中插入注释时，必须在注释前加上符号 "#"。make 读到 "#" 后，它忽略该符号以及这一行余下的字母。注释可以出现在 makefile 的所有地方。但是，因为多数 shell 把 "#" 看作是元符号（通常也是注释符），所以在命令中加入注释时要特别小心。此外，实际上就 make 本身而言，一个只含注释的行就是一个空行。</p><h2 id="命令行选项和参数">4 命令行选项和参数</h2><p>同多数 GNU 程序一样，make 也有丰富的命令行选项。表 4.3 列出了最常用的部分。</p><p>表 4.3 常用的 make 俞令行选项</p><table><thead><tr class="header"><th>选项</th><th>说明</th></tr></thead><tbody><tr class="odd"><td><code>-f file</code></td><td>指定 makefile 的文件名</td></tr><tr class="even"><td><code>-n</code></td><td>打印将需要执行的命令，但实际上并不执行这些命令</td></tr><tr class="odd"><td><code>-Idirname</code></td><td>指定被包含的 makefile 所在的目录</td></tr><tr class="even"><td><code>-s</code></td><td>在执行时不打印命令名</td></tr><tr class="odd"><td><code>-w</code></td><td>如果 make 在执行时改变目录，打印当前目录名</td></tr><tr class="even"><td><code>-Wfile</code></td><td>如果文件己修改，则使用 <code>-n</code> 来显示 make 将要执行的命令</td></tr><tr class="odd"><td><code>-r</code></td><td>禁止使用所有 make 的内置规则</td></tr><tr class="even"><td><code>-d</code></td><td>打印调试信息</td></tr><tr class="odd"><td><code>-i</code></td><td>忽略 makefile 规则中的命令执行后返回的非零错误码。此时，即使某个命令返回非零的退出状态值，make 仍将继续执行</td></tr><tr class="even"><td><code>-k</code></td><td>如果某个目标编译失败，继续编译其他目标。通常，make 在一个目标编译失败后终止</td></tr><tr class="odd"><td><code>jN</code></td><td>每次运行 N 条命令，这里 N 是非零整数</td></tr></tbody></table><h2 id="调试-make">5 调试 make</h2><p>如果在使用 make 时遇到问题，<code>-d</code> 选项能够使 make 在执行命令时打印大量的额外调试信息。此时，因为需要显示 make 内部所做的每一件事以及为什么做这些事的调试信息，将会产生大量的输出。其中包括如下信息：</p><ul><li>在重新编译时 make 需要检查的文件</li><li>被比较的文件以及比较的结果</li><li>需要被重新生成的文件</li><li>make 想要使用的隐式规则</li><li>make 实际使用的隐式规则以及所执行的命令</li></ul><h2 id="常见的-make-出错信息">6 常见的 make 出错信息</h2><p>这里列出使用 make 时可能遇到的最常用的出错信息，完整文档诮参见 make 使用手册或其信息页。</p><ul><li>No rule to make target 'target'. Stop makefile 中没有包含创建指定的 target 所需要的规则，而且也没有合适的默认规则可用。</li><li>'target' is up to date 指定 target 的相关文件没有变化。</li><li>Target 'target' not remade because of errors 在编译 target 时出错，这一消息仅在使用make的 -k 选项时才会出现。</li><li>command: Command not found make 找不到命令。递常是因为命令被拼写错误或者不在路径 <code>$PATH</code> 下。</li><li>Illegal option -option 在调用 make 时包含了不能被 make 识别的选项。</li></ul><h2 id="有用的-makefile-目标">7 有用的 makefile 目标</h2><p>除了前面提及的 clean，编写 makefile 时还有一些常用的目标。名为 install 的目标把最终的二进制文件，所支持的库文件或 shell 脚本，以及相关的文档移动到文件系统中与之相应的最终位置，并适当设置文件的权限和属主。此外，install 通常也编译程序，以及运行简单的测试以确认程序已正确编译。uninstall 目标则删除由 install 目标所安装的那些文件。如果需要，在设置 install 目标前存储系统当前的设置。</p><p>dist 目标可以用来生成准备发布的软件包。最低限度，dist 目标将删除编译工作目录中旧的二进制文件和目标文件并创建一个归档文件（如普通的压缩包），以便上传到万维网页或FTP站点。</p><p>为了方便其他开发者，可以用一个 tags 目标来创建或更新程序的标记表。如果程序的验证过程比较复杂，也可以创建一个 单独的 test 或 check 目标来执行这一过程并显示适当的诊断信息。与之类似，installtest 或 installcheck 目标，通常被用来验证安装过程。当然，在此之前，install 目标必须已经成功地编译和安装了所需的程序。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在本文中读者会看到有关 make 的介绍，make 是一种控制编译或者重复编译软件的工具。make 可以自动管理软件编译的内容、方式和时机，从而使程序员能够把精力集中在编写代码上。&lt;/p&gt;</summary>
    
    
    
    <category term="programming-language" scheme="https://www.wylu.me/categories/programming-language/"/>
    
    <category term="c" scheme="https://www.wylu.me/categories/programming-language/c/"/>
    
    
    <category term="make" scheme="https://www.wylu.me/tags/make/"/>
    
    <category term="makefile" scheme="https://www.wylu.me/tags/makefile/"/>
    
  </entry>
  
  <entry>
    <title>差分序列</title>
    <link href="https://www.wylu.me/posts/eaa75cf0/"/>
    <id>https://www.wylu.me/posts/eaa75cf0/</id>
    <published>2020-09-20T14:02:20.000Z</published>
    <updated>2023-04-08T10:01:01.782Z</updated>
    
    <content type="html"><![CDATA[<p>本文将介绍差分序列的定义及其应用。</p><span id="more"></span><h1 id="差分序列数组">差分序列（数组）</h1><h2 id="差分序列的定义">差分序列的定义</h2><p>给定一个序列 a, a[i] 表示序列的第 i 个元素（编号从 0 开始），则其差分序列为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">d[0] = a[0]  (i == 0)</span><br><span class="line">d[i] = a[i] - a[i-1]  (i &gt; 0)</span><br></pre></td></tr></table></figure><h2 id="差分序列示例">差分序列示例</h2><table><thead><tr class="header"><th>索引 i</th><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th></tr></thead><tbody><tr class="odd"><td>原始序列 a[i]</td><td>0</td><td>2</td><td>5</td><td>4</td><td>9</td><td>7</td><td>10</td><td>0</td></tr><tr class="even"><td>差分序列 d[i]</td><td>0</td><td>2</td><td>3</td><td>-1</td><td>5</td><td>-2</td><td>3</td><td>-10</td></tr></tbody></table><p>现在假设将区间 a[1, 4] 所有的数都加上 3，则：</p><table><thead><tr class="header"><th>索引 i</th><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th></tr></thead><tbody><tr class="odd"><td>原始序列 a[i]</td><td>0</td><td>2+3=5</td><td>5+3=8</td><td>4+3=7</td><td>9+3=12</td><td>7</td><td>10</td><td>0</td></tr><tr class="even"><td>差分序列 d[i]</td><td>0</td><td>2+3=5</td><td>3</td><td>-1</td><td>5</td><td>-2-3=-5</td><td>3</td><td>-10</td></tr></tbody></table><p>然后将区间 a[3, 5] 所有的数都减去 5，则：</p><table><thead><tr class="header"><th>索引 i</th><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th><th>7</th></tr></thead><tbody><tr class="odd"><td>原始序列 a[i]</td><td>0</td><td>2+3=5</td><td>5+3=8</td><td>4+3=7-5=2</td><td>9+3=12-5=7</td><td>7-5=2</td><td>10</td><td>0</td></tr><tr class="even"><td>差分序列 d[i]</td><td>0</td><td>2+3=5</td><td>3</td><td>-1-5=-6</td><td>5</td><td>-2-3=-5</td><td>3+5=8</td><td>-10</td></tr></tbody></table><h2 id="差分序列的性质">差分序列的性质</h2><p>仔细观察上面的示例，可以发现：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a[2] = d[2] + a[1]</span><br><span class="line">     = d[2] + d[1] + a[0]</span><br><span class="line">     = d[2] + d[1] + d[0]</span><br></pre></td></tr></table></figure><p>根据定义，对原序列区间操作后，原序列最终各项值为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">a[0] = d[0]  (i == 0)</span><br><span class="line">a[i] = d[i] + a[i-1]  (i &gt; 0)</span><br></pre></td></tr></table></figure><p>也即 a[i] 为 d[i] 的前缀和：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">a[i] = d[0] + ... + d[i]</span><br></pre></td></tr></table></figure><h2 id="差分序列的应用">差分序列的应用</h2><p><strong>基于差分序列的性质，它主要用于快速处理区间加减操作和单点查询</strong></p><h3 id="快速处理区间加减操作">快速处理区间加减操作</h3><p>假如现在对 a[L, R] 区间上所有的数加上 delta，由以上的性质可知，第一个受影响的差分数组中的元素为 d[L]，即令 d[L] += delta，那么后面数列元素在计算过程中都会加上 delta；最后一个受影响的差分数组中的元素为d[R]，所以令 d[R+1] -= delta，即可保证不会影响到 R 以后数列元素的计算。这样我们不必对区间内每一个数进行处理，只需处理两个差分后的数即可。</p><p>简单来说，当对 a[L, R] 区间所有数进行加 delta 操作时，只需:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">d[L] += delta</span><br><span class="line">d[R+1] -= delta</span><br></pre></td></tr></table></figure><p>那么此时计算 a[L] ... a[R] 都会加上 delta，这里用 (d[L] + delta) 表示归约后的 d[L]，则有：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">a[L] = (d[L] + delta) + a[L-1]</span><br><span class="line">a[L+1] = d[L+1] + a[L] = d[L+1] + (d[L] + delta) + a[L-1]</span><br><span class="line">...</span><br><span class="line">a[R] = d[R] + a[R-1] = d[R] + ... + (d[L] + delta) + a[L-1]</span><br></pre></td></tr></table></figure><h3 id="单点查询求差分序列前缀和">单点查询（求差分序列前缀和）</h3><h4 id="改变-d-数组">改变 d 数组</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, n):</span><br><span class="line">    d[i] += d[i-<span class="number">1</span>]</span><br></pre></td></tr></table></figure><h4 id="改变-a-数组">改变 a 数组</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a[<span class="number">0</span>] = d[<span class="number">0</span>]</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, n):</span><br><span class="line">    a[i] = d[i] + a[i-<span class="number">1</span>]</span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://www.cnblogs.com/COLIN-LIGHTNING/p/8436624.html">https://www.cnblogs.com/COLIN-LIGHTNING/p/8436624.html</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文将介绍差分序列的定义及其应用。&lt;/p&gt;</summary>
    
    
    
    <category term="algorithm" scheme="https://www.wylu.me/categories/algorithm/"/>
    
    <category term="math" scheme="https://www.wylu.me/categories/algorithm/math/"/>
    
    
    <category term="math" scheme="https://www.wylu.me/tags/math/"/>
    
  </entry>
  
  <entry>
    <title>并查集</title>
    <link href="https://www.wylu.me/posts/c517589e/"/>
    <id>https://www.wylu.me/posts/c517589e/</id>
    <published>2020-09-20T08:22:34.000Z</published>
    <updated>2023-04-08T09:59:40.212Z</updated>
    
    <content type="html"><![CDATA[<p>在计算机科学中，并查集是一种树型的数据结构，用于处理一些不交集（Disjoint Sets）的合并及查询问题。有一个联合-查找算法（union-find algorithm）定义了两个用于此数据结构的操作：</p><ul><li>Find：确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。</li><li>Union：将两个子集合并成同一个集合。</li></ul><p>由于支持这两种操作，一个不相交集也常被称为联合-查找数据结构（union-find data structure）或合并-查找集合（merge-find set）。其他的重要方法，MakeSet，用于建立单元素集合。有了这些方法，许多经典的划分问题可以被解决。</p><span id="more"></span><h1 id="并查集">并查集</h1><h2 id="并查集是什么">并查集是什么</h2><p>并查集是一种用来管理元素分组情况的数据结构。并查集可以高效地进行如下操作。不过需要注意并查集虽然可以进行合并操作，但是却无法进行分割操作。</p><ul><li>查询元素 a 和元素 b 是否属于同一组。</li><li>合并元素 a 和元素 b 所在的组。</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/a1e24844edcc38ebe52397ba94cf382d.png" alt="uf-func" /></p><h2 id="并查集的结构">并查集的结构</h2><p>并查集也是树形结构实现的。不过，不是二叉树。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/d695760169a280d25e7442f96d7afdbf.png" alt="uf-struct" /></p><p>每个元素对应一个节点，每个组对应一棵树。在并查集中，哪个节点是哪个节点的父亲以及树的形状等信息无需多加关注，整体组成一个树形结构才是重要的。</p><p>（1）初始化</p><p>我们准备 n 个节点来表示 n 个元素。最开始时没有边。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/b61b4b34b1278c478fadf7ceddef0a17.png" alt="uf-initial-state" /></p><p>（2）合并</p><p>像下图一样，从一个组的根向另一个组的根连边，这样两棵树就变成了一棵树，也就把两个组合并为一个组了。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/c1fa7ad7ca0c1a199c4e1ef16edac35a.png" alt="uf-union" /></p><p>（3）查询</p><p>为了查询两个节点是否属于同一组，我们需要沿着树向上走，来查询包含这个元素的树的根是谁。如果两个节点走到了同一个根，那么就可以知道它们属于同一组。</p><p>在下图中，元素 2 和元素 5 都走到了元素 1，因此它们属于同一组。另一方面，由于元素 7 走到的是元素 6，因此同元素 2 或元素 5 属于不同组。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/81241fe164c49bca00235dffb8269cb7.png" alt="uf-find" /></p><h2 id="并查集实现中的注意点">并查集实现中的注意点</h2><p>在树形数据结构里，如果发生了退化的情况，那么复杂度就会变得很高。因此，有必要想办法避免退化的发生。在并查集中，只需按照如下方法就可以避免退化。</p><ul><li>对于每棵树，记录这棵树的高度(rank)。</li><li>合并时如果两棵树的 rank 不同，那么从 rank 小的向 rank 大的连边。</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/4844beffb80851e3db14bdf1e095e8e7.png" alt="uf-union-based-rank" /></p><p>此外，通过路径压缩，可以使得并查集更加高效。对于每个节点，一旦向上走到了一次根节点，就把这个点到父亲的边改为直接连向根。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/3f76bfcaad7e4c16ee1e14035a15a234.png" alt="uf-path-compress-1" /></p><p>在此之上，不仅仅是所查询的节点，在查询过程中向上经过的所有的节点，都改为直接连到根上。这样再次查询这些节点时，就可以很快知道根是谁了。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/0946c0e632dac3e08e1581769f85708a.png" alt="uf-path-compress-2" /></p><p>在使用这种简化的方法时，为了简单起见，即使树的高度发生了变化，我们也不修改 rank 的值。</p><h2 id="并查集的复杂度">并查集的复杂度</h2><p>加入了这两个优化之后的并查集效率非常高。对 n 个元素的并查集进行一次操作的复杂度是 <span class="math inline">\(O(\alpha(n))\)</span>。在这里，<span class="math inline">\(\alpha(n)\)</span> 是阿克曼( Ackermann )函数的反函数。这比 <span class="math inline">\(O(log(n))\)</span> 还要快。</p><p>不过，这是“均摊复杂度”。也就是说，并不是每一次操作都满足这个复杂度，而是多次操作之后平均每一次操作的复杂度是 <span class="math inline">\(O(\alpha(n))\)</span> 的意思。</p><h2 id="并查集的实现">并查集的实现</h2><p>下面是并查集的实现的例子。在例子中，我们用编号代表每个元素。数组 par 表示的是父亲的编号，par[x] = x 时, x 是所在的树的根。</p><h3 id="路径压缩">路径压缩</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">UnionFind</span>:</span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, n: <span class="built_in">int</span></span>):</span><br><span class="line">        self.par = <span class="built_in">list</span>(<span class="built_in">range</span>(n))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find</span>(<span class="params">self, x: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        <span class="keyword">if</span> self.par[x] != x:</span><br><span class="line">            self.par[x] = self.find(self.par[x])</span><br><span class="line">        <span class="keyword">return</span> self.par[x]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">union</span>(<span class="params">self, x: <span class="built_in">int</span>, y: <span class="built_in">int</span></span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        self.par[self.find(x)] = self.find(y)</span><br></pre></td></tr></table></figure><h3 id="路径压缩-按秩rank合并">路径压缩 + 按秩（rank）合并</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">UnionFind</span>:</span><br><span class="line">    <span class="comment"># 初始化 n 个元素</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, n: <span class="built_in">int</span></span>):</span><br><span class="line">        self.par = <span class="built_in">list</span>(<span class="built_in">range</span>(n))  <span class="comment"># 祖先结点</span></span><br><span class="line">        self.rank = [<span class="number">0</span>] * n  <span class="comment"># 树的高度</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 查询树的根</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">find</span>(<span class="params">self, x: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">        <span class="keyword">if</span> self.par[x] != x:</span><br><span class="line">            self.par[x] = self.find(self.par[x])</span><br><span class="line">        <span class="keyword">return</span> self.par[x]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 合并 x 和 y 所属的集合</span></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">union</span>(<span class="params">self, x: <span class="built_in">int</span>, y: <span class="built_in">int</span></span>) -&gt; <span class="literal">None</span>:</span><br><span class="line">        x = self.find(x)</span><br><span class="line">        y = self.find(y)</span><br><span class="line">        <span class="keyword">if</span> x == y:</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> self.rank[x] &lt; self.rank[y]:</span><br><span class="line">            self.par[x] = y</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            self.par[y] = x</span><br><span class="line">            <span class="keyword">if</span> self.rank[x] == self.rank[y]:</span><br><span class="line">                self.rank[x] += <span class="number">1</span></span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://en.wikipedia.org/wiki/Disjoint-set_data_structure">https://en.wikipedia.org/wiki/Disjoint-set_data_structure</a></p><p>&lt;&lt;挑战程序设计竞赛(第2版)&gt;&gt; 巫泽俊 2.4 并查集 p84-88</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在计算机科学中，并查集是一种树型的数据结构，用于处理一些不交集（Disjoint Sets）的合并及查询问题。有一个联合-查找算法（union-find algorithm）定义了两个用于此数据结构的操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find：确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。&lt;/li&gt;
&lt;li&gt;Union：将两个子集合并成同一个集合。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由于支持这两种操作，一个不相交集也常被称为联合-查找数据结构（union-find data structure）或合并-查找集合（merge-find set）。其他的重要方法，MakeSet，用于建立单元素集合。有了这些方法，许多经典的划分问题可以被解决。&lt;/p&gt;</summary>
    
    
    
    <category term="algorithm" scheme="https://www.wylu.me/categories/algorithm/"/>
    
    <category term="data-structure" scheme="https://www.wylu.me/categories/algorithm/data-structure/"/>
    
    
    <category term="并查集" scheme="https://www.wylu.me/tags/%E5%B9%B6%E6%9F%A5%E9%9B%86/"/>
    
  </entry>
  
  <entry>
    <title>RabbitMQ高可用集群搭建</title>
    <link href="https://www.wylu.me/posts/85807abd/"/>
    <id>https://www.wylu.me/posts/85807abd/</id>
    <published>2020-07-16T14:28:26.000Z</published>
    <updated>2023-07-26T14:43:11.445Z</updated>
    
    <content type="html"><![CDATA[<p>RabbitMQ + HAProxy 高可用镜像模式集群部署</p><p>本文介绍了如何搭建 RabbitMQ 高可用集群。首先介绍了部署说明和前提条件，然后详细介绍了集群模式和节点类型。接着介绍了环境准备和集群搭建的步骤，包括普通模式和镜像模式的配置。然后介绍了集群节点管理和故障处理的方法。最后介绍了使用 HAProxy 负载均衡的方法，并提供了安装、配置和验证的步骤。最后列举了一些常见问题。</p><span id="more"></span><h1 id="rabbitmq-高可用集群搭建">RabbitMQ 高可用集群搭建</h1><h2 id="部署说明">部署说明</h2><p>下面将以一个示例说明多机部署一个高可用 RabbitMQ 集群的流程。</p><h3 id="前提">前提</h3><p>在部署集群前，必须在将成为集群成员的每个节点上安装 RabbitMQ，并确保每个节点之间能相互访问。</p><h3 id="rabbitmq-集群模式">RabbitMQ 集群模式</h3><ul><li><p>普通模式（默认的集群模式）</p><p>以两个节点 node1、node2 为例来进行说明。</p><p>对于 queue 来说，消息实体只存在于其中一个节点（node1 或 node2），node1 和 node2 两个节点仅有相同的元数据，即队列的结构。当消息进入 node1 节点的 queue 后，consumer 从 node2 节点消费时，RabbitMQ 会临时在 node1、node2 间进行消息传输，把 node1 中的消息实体取出并经过 node2 发送给 consumer。所以 consumer 应尽量连接每一个节点，从中取消息。即对于同一个逻辑队列，要在多个节点建立物理 queue。否则无论 consumer 连接 node1 或 node2，出口总在 node1，会产生瓶颈。</p><p>当 node1 节点故障后，node2 节点无法取到 node1 节点中还未消费的消息实体。如果做了消息持久化，那么得等 node1 节点恢复，然后才可被消费；如果没有持久化的话，就会产生消息丢失的现象。</p></li><li><p>镜像模式</p><p>把需要的队列做成镜像队列，存在于多个节点，属于 RabbiMQ 的 HA 方案，在对业务可靠性要求较高的场合中比较适用。</p><p>该模式解决了普通模式中的问题，其实质和普通模式不同之处在于，消息实体会主动在镜像节点间同步，而不是在客户端取数据时临时拉取。该模式带来的副作用也很明显，除了降低系统性能外，如果镜像队列数量过多，加之大量的消息进入，集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用。</p><p>要实现镜像模式，需要先搭建一个普通集群模式，在这个模式的基础上再配置策略以实现高可用。</p></li></ul><h3 id="集群节点类型">集群节点类型</h3><ul><li>内存（ram）节点</li><li>磁盘（disk）节点</li></ul><p>RAM 节点仅将内部数据库表存储在 RAM 中。这不包括消息，消息存储索引，队列索引和其他节点状态。</p><p>在大多数情况下，您希望所有节点都是磁盘节点。 RAM 节点是一种特殊情况，可用于提高 queue、exchange 或 binding 流失率较高的群集的性能。 RAM 节点不提供更高的消息速率。如有疑问，请仅使用磁盘节点。</p><p>由于 RAM 节点仅将内部数据库表存储在RAM中，因此它们必须在启动时从对等节点同步它们。这意味着一个群集必须至少包含一个磁盘节点。因此，不可能手动删除集群中最后剩余的磁盘节点。</p><p>在 RabbitMQ 集群中，当磁盘节点宕掉且集群中无其他可用的磁盘节点时，集群将无法写入新的队列元数据信息。</p><h3 id="环境准备">环境准备</h3><ul><li><p>系统系统：CentOS7 64位</p></li><li><p>三台服务器：192.168.0.231/232/233</p></li><li><p>服务器规划</p><table><thead><tr class="header"><th>服务器</th><th>用途</th><th>主机名</th><th>节点类型</th></tr></thead><tbody><tr class="odd"><td>192.168.0.231</td><td>RabbitMQ 集群节点 1</td><td>node231</td><td>磁盘节点</td></tr><tr class="even"><td>192.168.0.232</td><td>RabbitMQ 集群节点 2</td><td>node232</td><td>磁盘节点</td></tr><tr class="odd"><td>192.168.0.233</td><td>RabbitMQ 集群节点 3</td><td>node233</td><td>磁盘节点</td></tr></tbody></table></li></ul><h2 id="集群搭建">集群搭建</h2><h3 id="普通模式">普通模式</h3><h4 id="配置-hosts">配置 hosts</h4><p><code>vim /etc/hosts</code> 编辑三个节点的 hosts 文件，在文件末尾添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">192.168.0.231 node231</span><br><span class="line">192.168.0.232 node232</span><br><span class="line">192.168.0.233 node233</span><br></pre></td></tr></table></figure><h4 id="配置-hostname">配置 hostname</h4><ul><li><p>node231</p><p><code>vim /etc/hostname</code> 编辑主机名如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node231</span><br></pre></td></tr></table></figure><p><code>vim /etc/sysconfig/network</code> 编辑网络配置文件，添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">NETWORKING=yes</span><br><span class="line">HOSTNAME=node231</span><br></pre></td></tr></table></figure><p>重启 network</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart network</span><br></pre></td></tr></table></figure></li><li><p>node232</p><p><code>vim /etc/hostname</code> 编辑主机名如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node232</span><br></pre></td></tr></table></figure><p><code>vim /etc/sysconfig/network</code> 编辑网络配置文件，添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">NETWORKING=yes</span><br><span class="line">HOSTNAME=node232</span><br></pre></td></tr></table></figure><p>重启 network</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart network</span><br></pre></td></tr></table></figure></li><li><p>node233</p><p><code>vim /etc/hostname</code> 编辑主机名如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node233</span><br></pre></td></tr></table></figure><p><code>vim /etc/sysconfig/network</code> 编辑网络配置文件，添加如下内容：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">NETWORKING=yes</span><br><span class="line">HOSTNAME=node233</span><br></pre></td></tr></table></figure><p>重启 network</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart network</span><br></pre></td></tr></table></figure></li></ul><h4 id="配置-erlang-cookie">配置 erlang cookie</h4><p>RabbitMQ 集群是基于 erlang 进行同步的，在 erlang 的集群中各节点同步需要一个相同的 cookie，所以必须保证各节点 cookie 一致，不然节点之间就无法通信。这个 cookie 默认存放在 <code>/var/lib/rabbitmq/.erlang.cookie</code> 中。</p><p>在任意一个节点中 copy <code>.erlang.cookie</code> 文件到其它所有节点，如在 node1 上进行 copy :</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@node231 ~]# scp /var/lib/rabbitmq/.erlang.cookie root@192.168.0.232:/var/lib/rabbitmq/</span><br><span class="line">[root@node231 ~]# scp /var/lib/rabbitmq/.erlang.cookie root@192.168.0.233:/var/lib/rabbitmq/</span><br></pre></td></tr></table></figure><h4 id="重启节点">重启节点</h4><p><strong>如果后面执行 <code>rabbitmqctl stop_app</code> 失败，需要重启 node231、node232、node233 使配置生效。</strong></p><h4 id="启动-rabbitmq-server">启动 rabbitmq-server</h4><p>分别启动 node231、node232、node233 的 rabbitmq-server：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[root@node231 ~]# systemctl start rabbitmq-server</span><br><span class="line">[root@node232 ~]# systemctl start rabbitmq-server</span><br><span class="line">[root@node233 ~]# systemctl start rabbitmq-server</span><br></pre></td></tr></table></figure><h4 id="将节点加入集群">将节点加入集群</h4><p>将 node232、node233 节点加入 node231 节点集群中，<strong>在 node232、node233 中分别执行以下命令：</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">rabbitmqctl stop_app</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">rabbitmqctl join_cluster rabbit@node231</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">rabbitmqctl start_app</span></span><br></pre></td></tr></table></figure><ul><li><p>默认 RabbitMQ 启动后是磁盘节点，在这个 cluster 下，node231、node232 和 node233 都是是磁盘节点。</p></li><li><p>如果要使 node232、node233 都是内存节点，加上 <code>--ram</code> 参数即可，如 <code>rabbitmqctl join_cluster --ram rabbit@node232</code>。</p></li><li><p>如果想要更改节点类型，可以使用命令 <code>rabbitmqctl change_cluster_node_type disc(ram)</code>，修改节点类型前需要先 <code>rabbitmqctl stop_app</code>。</p><p>(Note: disk and disc are used interchangeably)</p></li></ul><h4 id="查看集群状态">查看集群状态</h4><p>任意节点执行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">rabbitmqctl cluster_status</span></span><br></pre></td></tr></table></figure><h4 id="创建管理用户">创建管理用户</h4><p>如果在主机名变更前就已经创建过用户的，仍需要重新重新创建，因为主机名的变更，之前创建的用户无法登录 web 管理系统。</p><p>以下操作在 node231 下执行：</p><ul><li><p>创建 vhost（可选，默认使用 "/" vhost）</p><p>这里创建一个 vhost 用于测试：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node231 ~]# rabbitmqctl add_vhost testvhost</span><br></pre></td></tr></table></figure></li><li><p>创建用户</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node231 ~]# add_user admin password</span><br></pre></td></tr></table></figure></li><li><p>设置用户角色</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node231 ~]# set_user_tags admin administrator</span><br></pre></td></tr></table></figure></li><li><p>设置用户权限</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node231 ~]# set_permissions -p testvhost admin &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;</span><br></pre></td></tr></table></figure></li></ul><h4 id="启用-rabbitmq-management">启用 rabbitmq management</h4><p>在 node231 上启用 rabbitmq management</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@node231 ~]# rabbitmq-plugins enable rabbitmq_management</span><br></pre></td></tr></table></figure><p>在浏览器中访问 <a href="http://192.168.0.231:15672" class="uri">http://192.168.0.231:15672</a>，使用 "admin/password" 即可登录。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/d818e23c38f500cf4f690d7d225b335b.png" alt="web-management" /></p><h3 id="镜像模式">镜像模式</h3><p>上面已经完成 RabbitMQ 默认集群模式，但并不保证队列的高可用性，尽管 Exchanges、Bindings 这些可以复制到集群里的任何一个节点，但是队列内容不会复制。所以集群中的节点宕机后将直接导致队列无法应用或消息丢失，要想队列在节点宕机或故障时也能正常应用，需要复制队列内容到集群中的每个节点，这就要使用镜像队列了。</p><p>为了确认队列内容默认不会复制，我们需要做些实验。</p><ol type="1"><li><p>首先我们在 node231 节点上创建一个名为 <code>demo_task</code> 的持久化队列（相关的 exchange 也是持久化的），这事可以看到队列的元信息会立即同步到 node232 和 node233 上。</p></li><li><p>然后我们连接到 node231 节点上发布一个消息，发布成功后，你可以在所有节点上获取到该消息。</p></li><li><p>然后我们使用 <code>systemctl stop rabbitmq-server</code> 命令将 node231 节点关闭，此时发现 node232 和 node233 虽然还保留了 <code>demo_task</code> 的元信息，但却无法从中获取消息了。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/ff93c9398bedeef15e8f9fc6165dfe9e.png" alt="node-down" /></p></li><li><p>当我们使用 <code>systemctl start rabbitmq-server</code> 命令再次将 node231 节点启动后，node232 和 node233 依然能看到 node231 关闭前已经持久化的消息。</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/9182032433d90bc8b18ce650ce496210.png" alt="node-run" /></p></li></ol><h4 id="镜像队列">镜像队列</h4><p>默认情况下，RabbitMQ 集群中 queue 的内容位于单个节点（声明该 queue 的节点）上。这与 exchanges 和 bindings 相反，exchanges 和 bindings 始终可以被视为在所有节点上。可以选择使 queue 跨多个节点进行镜像。</p><p>每个镜像队列由一个 master 和一个或多个镜像（mirrors）组成。master 托管在一个通常称为主节点的节点上。每个队列都有其自己的主节点。给定队列的所有操作都首先应用于队列的主节点，然后传播到镜像节点。这涉及排队发布，向消费者传递消息，跟踪来自消费者的确认等。</p><p>队列镜像意味着节点的集群。发布到队列的消息将复制到所有镜像。无论消费者连接到哪个节点，最终都会被连接到主节点，镜像节点都会丢弃已在主节点上确认的消息。因此，队列镜像可提高可用性，但不会在节点之间分配负载（所有参与的节点均完成所有工作）。</p><p>如果承载队列主节点发生故障，则最早的镜像将在同步后提升为新的主节点。根据队列镜像参数，也可以升级不同步的镜像。</p><h4 id="配置镜像策略">配置镜像策略</h4><p>使用策略（policiy）配置镜像参数。 一个策略按名称（使用正则表达式模式）匹配一个或多个队列，并且包含一个定义（可选参数的映射），该定义被添加到匹配队列的全部属性中。</p><h5 id="通过控制台添加策略">通过控制台添加策略</h5><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/a5048354dbb6871c1e18317b8caba656.png" alt="add-policy" /></p><p>如果其他节点也启用了 <code>rabbitmq_management</code>，此时其他节点的控制台，可以看到上面添加的这个策略，如图所示：</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/e367945975a29d2b2acff4ccbe579b53.png" alt="show-policies" /></p><p>参数说明：</p><ul><li><p>Virtual host：策略应用的 vhost。</p></li><li><p>Name：为策略名称，可以是任何东西，但建议使用不带空格的基于ASCII的名称。</p></li><li><p>Pattern：与一个或多个 queue（exchange） 名称匹配的正则表达式，可以使用任何正则表达式。只有一个 <code>^</code> 代表匹配所有，<code>^test</code> 为匹配名称为 "test" 的 exchanges 或者 queue。</p></li><li><p>Apply to：Pattern 应用对象。</p></li><li><p>Priority：配置了多个策略时候的优先级，值越大，优先级越高。</p><p>（没有指定优先级的消息会以0优先级对待。对于超过队列所定最大优先级的消息，优先级以最大优先级对待）</p></li><li><p>Definition：一组键/值对（例如 JSON 文档），将被插入匹配 queues and exchanges 的可选参数映射中</p><p><code>ha-mode</code>：策略键，分为3种模式</p><ul><li><code>all</code> - 所有（所有的 queue）</li><li><code>exctly</code> - 部分（需配置 <code>ha-params</code> 参数，此参数为 int 类型。比如 3，众多集群中的随机 3 台机器）</li><li><code>nodes</code> - 指定（需配置 <code>ha-params</code> 参数，此参数为数组类型。比如 ["rabbit@node2", "rabbit@node3"] 这样指定为 node2 与 node3 这两台机器）</li></ul><p><code>ha-sync-mode</code>：队列同步</p><ul><li><code>manual</code>：手动（默认模式）。新的队列镜像将不会收到现有的消息，它只会接收新的消息</li><li><code>automatic</code>：自动同步。当一个新镜像加入时，队列会自动同步。队列同步是一个阻塞操作。</li></ul></li></ul><h5 id="通过命令行添加策略">通过命令行添加策略</h5><ul><li><p>设置</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl set_policy [-p &lt;vhost&gt;] [--priority &lt;priority&gt;] [--apply-to &lt;apply-to&gt;] &lt;name&gt; &lt;pattern&gt;  &lt;definition&gt;</span><br></pre></td></tr></table></figure></li><li><p>清除</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl clear_policy [-p &lt;vhost&gt;] &lt;name&gt;</span><br></pre></td></tr></table></figure></li><li><p>查看</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl list_policies [-p &lt;vhost&gt;]</span><br></pre></td></tr></table></figure></li></ul><p>例如：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">rabbitmqctl set_policy -p testvhost testha <span class="string">&quot;^&quot;</span> <span class="string">&#x27;&#123;&quot;ha-mode&quot;:&quot;all&quot;,&quot;ha-sync-mode&quot;:&quot;automatic&quot;&#125;&#x27;</span></span></span><br><span class="line">Setting policy &quot;testha&quot; for pattern &quot;^&quot; to &quot;&#123;&quot;ha-mode&quot;:&quot;all&quot;,&quot;ha-sync-mode&quot;:&quot;automatic&quot;&#125;&quot; with priority &quot;0&quot; for vhost &quot;testvhost&quot; ...</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">rabbitmqctl list_policies -p testvhost</span></span><br><span class="line">Listing policies for vhost &quot;testvhost&quot; ...</span><br><span class="line">vhost   name    pattern apply-to        definition      priority</span><br><span class="line">testvhost       testha  ^       all     &#123;&quot;ha-mode&quot;:&quot;all&quot;,&quot;ha-sync-mode&quot;:&quot;automatic&quot;&#125;    0</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">rabbitmqctl clear_policy -p testvhost testha</span></span><br><span class="line">Clearing policy &quot;testha&quot; on vhost &quot;testvhost&quot; ...</span><br></pre></td></tr></table></figure><h4 id="测试策略是否生效">测试策略是否生效</h4><p>在控制台的队列页面上，镜像队列将展示策略名称和其他副本（镜像）数量。以下是一个名为 three_replicas 的队列的示例，该队列具有一个 master（主节点）和两个镜像节点。</p><p>添加队列：</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/26fc106e142b4edf15211d559de61272.png" alt="add-queue" /></p><p>查看队列：</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/599f2303ec6440b0b7758030b7069ec4.png" alt="show-queues" /></p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/5c42a840bfb1534a474d46b2b870e915.png" alt="queue-detail" /></p><h2 id="集群节点管理">集群节点管理</h2><h3 id="故障节点重新加入集群">故障节点重新加入集群</h3><p><strong>删除 /var/lib/rabbitmq/mnesia/ 下的数据</strong>，重新启动 rabbitmq-server，正常执行节点增加操作。</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rm -rf /var/lib/rabbitmq/mnesia/</span><br><span class="line">systemctl restart rabbitmq-server.service</span><br></pre></td></tr></table></figure><h3 id="节点增加">节点增加</h3><p>注：需要同步 <code>.erlang.cooike</code> 内容（<code>.erlang.cooike</code> 的权限为 400，所属者为 rabbitmq）</p><p>在需要加入的节点机器上执行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl stop_app</span><br><span class="line">rabbitmqctl join_cluster --ram rabbit@node231</span><br><span class="line">rabbitmqctl start_app</span><br><span class="line">rabbitmqctl cluster_status</span><br></pre></td></tr></table></figure><h3 id="节点删除">节点删除</h3><h4 id="正常删除">正常删除</h4><p>在需要删除的节点（rabbitmq-server 正常运行）机器上执行：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl stop_app</span><br><span class="line">rabbitmqctl reset</span><br><span class="line">rabbitmqctl start_app</span><br></pre></td></tr></table></figure><h4 id="硬删除">硬删除</h4><p>在集群正常节点将故障节点踢出，在其它正常的节点上执行（故障节点 rabbitmq-server 服务不可用）：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl forget_cluster_node rabbit@node232</span><br></pre></td></tr></table></figure><h3 id="改变节点类型">改变节点类型</h3><h4 id="disc---ram">disc -&gt; ram</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl stop_app</span><br><span class="line">rabbitmqctl reset</span><br><span class="line">rabbitmqctl join_cluster --ram rabbit@node231</span><br><span class="line">rabbitmqctl start_app</span><br></pre></td></tr></table></figure><h4 id="ram---disc">ram -&gt; disc</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">rabbitmqctl stop_app</span><br><span class="line">rabbitmqctl reset</span><br><span class="line">rabbitmqctl join_cluster rabbit@node231</span><br><span class="line">rabbitmqctl start_app</span><br></pre></td></tr></table></figure><h2 id="haproxy-负载均衡">HAProxy 负载均衡</h2><p>HAProxy 是由 C 语言编写的免费的开源软件，它快速而高效，可为基于TCP和HTTP的应用程序提供高可用、高性能的负载平衡器和代理服务器。</p><h3 id="环境准备-1">环境准备</h3><table><thead><tr class="header"><th>服务器</th><th>用途</th><th>系统</th><th>版本</th></tr></thead><tbody><tr class="odd"><td>192.168.0.235</td><td>负载均衡服务器</td><td>Ubuntu 20.04</td><td>haproxy 2.0</td></tr></tbody></table><h3 id="安装">安装</h3><h4 id="ubuntu">Ubuntu</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo apt install haproxy</span></span><br></pre></td></tr></table></figure><h4 id="centos-7">CentOS 7</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">wget http://www.haproxy.org/download/1.7/src/haproxy-1.7.12.tar.gz</span><br><span class="line">tar -zxvf haproxy-1.7.12.tar.gz</span><br><span class="line">cd haproxy-1.7.12</span><br></pre></td></tr></table></figure><p>根据内核版本，选择编译参数</p><p>查看内核版本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[root@dev235 haproxy-1.7.12]# uname -r</span><br><span class="line">3.10.0-1127.19.1.el7.x86_64</span><br></pre></td></tr></table></figure><p>如：<code>3.10.0-1127.19.1.el7.x86_64</code>，此时该参数就为 <code>linux310</code>；kernel 大于 2.6.28 的可以用： <code>TARGET=linux2628</code>；</p><p>查看编译参数：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[root@dev235 haproxy-1.7.12]# less README</span><br></pre></td></tr></table></figure><p>编译安装：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">make TARGET=linux310 ARCH=x86_64 PREFIX=/usr/local/haproxy</span><br><span class="line">make install PREFIX=/usr/local/haproxy</span><br></pre></td></tr></table></figure><ul><li><code>TARGET=linux310</code>，内核版本，如：<code>3.10.0-1127.19.1.el7.x86_64</code>，此时该参数就为 <code>linux310</code>；kernel 大于 2.6.28 的可以用： <code>TARGET=linux2628</code>；</li><li><code>ARCH=x86_64</code>，系统位数；</li><li><code>PREFIX=/usr/local/haprpxy</code> ，haproxy 安装路径；</li></ul><h5 id="使用-systemclt-管理-haproxy">使用 systemclt 管理 haproxy</h5><p>在 <code>haproxy-1.7.12</code> 中有 systemd 脚本可以使用：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">[root@dev235 haproxy-1.7.12]# cat contrib/systemd/haproxy.service.in</span><br><span class="line">[Unit]</span><br><span class="line">Description=HAProxy Load Balancer</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Environment=&quot;CONFIG=/etc/haproxy/haproxy.cfg&quot; &quot;PIDFILE=/run/haproxy.pid&quot;</span><br><span class="line">ExecStartPre=@SBINDIR@/haproxy -f $CONFIG -c -q</span><br><span class="line">ExecStart=@SBINDIR@/haproxy-systemd-wrapper -f $CONFIG -p $PIDFILE</span><br><span class="line">ExecReload=@SBINDIR@/haproxy -f $CONFIG -c -q</span><br><span class="line">ExecReload=/bin/kill -USR2 $MAINPID</span><br><span class="line">KillMode=mixed</span><br><span class="line">Restart=always</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure><p>copy 到 /lib/systemd/system 下，并修改 <code>@SBINDIR@</code> 为 /usr/local/haproxy/sbin，将 <code>haproxy-systemd-wrapper</code> 替换为 <code>haproxy</code>：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cp contrib/systemd/haproxy.service.in /lib/systemd/system/haproxy.service</span><br><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl restart haproxy.service</span><br></pre></td></tr></table></figure><h3 id="配置">配置</h3><p><code>vim /etc/haproxy/haproxy.cfg</code> 编辑 haproxy 配置文件，修改如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">global</span><br><span class="line">    maxconn 512</span><br><span class="line">    stats socket /tmp/haproxy</span><br><span class="line"></span><br><span class="line">defaults</span><br><span class="line">    log global</span><br><span class="line">    mode http</span><br><span class="line">    option abortonclose</span><br><span class="line">    compression algo gzip</span><br><span class="line">    compression type text/html text/plain application/json</span><br><span class="line">    timeout connect 5000ms</span><br><span class="line">    # timeout connect 3600s</span><br><span class="line"></span><br><span class="line">listen stats</span><br><span class="line">    bind *:8888</span><br><span class="line">    mode http</span><br><span class="line">    log 127.0.0.1 local3 err</span><br><span class="line">    stats refresh 60s</span><br><span class="line">    stats uri /stats</span><br><span class="line">    stats realm Haproxy\ Manager</span><br><span class="line">    stats auth admin:password</span><br><span class="line">    stats hide-version</span><br><span class="line">    stats admin if TRUE</span><br><span class="line"></span><br><span class="line">listen rabbitmq_cluster</span><br><span class="line">    bind *:5672</span><br><span class="line">    mode tcp</span><br><span class="line">    option tcpka</span><br><span class="line">    balance roundrobin</span><br><span class="line">    server rabbit1 192.168.0.231:5672 check inter 1000 rise 2 fall 3 weight 1</span><br><span class="line">    server rabbit2 192.168.0.232:5672 check inter 1000 rise 2 fall 3 weight 1</span><br><span class="line">    server rabbit3 192.168.0.233:5672 check inter 1000 rise 2 fall 3 weight 1</span><br></pre></td></tr></table></figure><h3 id="验证-haproxy-配置">验证 HAProxy 配置</h3><p>修改配置后，在启动 HAProxy 前，应先运行以下命令验证配置文件语法：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">haproxy -f /etc/haproxy/haproxy.cfg -c -V</span></span><br><span class="line">Configuration file is valid</span><br></pre></td></tr></table></figure><p>如果收到错误消息，请务必先修复，然后再继续。</p><h3 id="运行-haproxy">运行 HAProxy</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">systemctl restart haproxy</span></span><br></pre></td></tr></table></figure><p>查看状态：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">systemctl status haproxy</span></span><br></pre></td></tr></table></figure><h3 id="haproxy-statistics">HAProxy Statistics</h3><p>浏览器访问 <a href="http://192.168.0.235:8888/stats" class="uri">http://192.168.0.235:8888/stats</a>，输入配置中的用户名和密码登录：</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/31b6b0444f710e6c34773344c1ce13dd.png" alt="haproxy-statistics" /></p><h2 id="常见问题">常见问题</h2><h3 id="新节点-join_cluster-失败">1. 新节点 join_cluster 失败</h3><p><strong>问题描述：</strong></p><p>新创建的 RabbitMQ 节点，在 join_cluster 时失败，提示如下信息：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root@node232 ~]# rabbitmqctl join_cluster rabbit@node231.com</span><br><span class="line">Clustering node rabbit@node232 with rabbit@node231.com</span><br><span class="line">Error:</span><br><span class="line">&#123;:badrpc_multi, &#123;:EXIT, &#123;&#123;:function_clause, [&#123;:gen, :do_for_proc, [&#123;:rex, &#123;:error, &#123;:node_name, :short&#125;&#125;&#125;, #Function&lt;0.9801092/1 in :gen.call/4&gt;], [file: &#x27;gen.erl&#x27;, line: 220]&#125;, &#123;:gen_server, :call, 3, [file: &#x27;gen_server.erl&#x27;, line: 219]&#125;, &#123;:rpc, :do_call, 3, [file: &#x27;rpc.erl&#x27;, line: 327]&#125;, &#123;:lists, :foldl, 3, [file: &#x27;lists.erl&#x27;, line: 1263]&#125;, &#123;:rabbit_mnesia, :discover_cluster, 1, [file: &#x27;src/rabbit_mnesia.erl&#x27;, line: 779]&#125;, &#123;:rabbit_mnesia, :join_cluster, 2, [file: &#x27;src/rabbit_mnesia.erl&#x27;, line: 212]&#125;, &#123;:rpc, :&quot;-handle_call_call/6-fun-0-&quot;, 5, [file: &#x27;rpc.erl&#x27;, line: 197]&#125;]&#125;, &#123;:gen_server, :call, [&#123;:rex, &#123;:error, &#123;:node_name, :short&#125;&#125;&#125;, &#123;:call, :rabbit_mnesia, :cluster_status_from_mnesia, [], #PID&lt;0.62.0&gt;&#125;, :infinity]&#125;&#125;&#125;, [error: &#123;:node_name, :short&#125;]&#125;</span><br></pre></td></tr></table></figure><p><strong>问题原因：</strong></p><p><code>rabbit@hostname</code> ，hostname 不允许包含 <code>.</code> 字符，如果主机名是 <code>node231.com</code>，则应该在 <code>/etc/hosts</code> 同时指定 <code>node231</code> 映射的 IP。</p><p><strong>解决方法：</strong></p><ol type="1"><li><p>编辑 <code>/etc/hosts</code> 文件，添加 <code>192.168.0.231 node231</code>。</p></li><li><p>使用 <code>rabbit@node231</code> 而不是 <code>rabbit@node231.com</code>。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@node232 ~]# rabbitmqctl join_cluster rabbit@node231</span><br><span class="line">Clustering node rabbit@node232 with rabbit@node231</span><br><span class="line">[root@node232 ~]# rabbitmqctl start_app</span><br><span class="line">Starting node rabbit@node232 ...</span><br><span class="line"> completed with 3 plugins.</span><br></pre></td></tr></table></figure></li><li><p>查看集群状态。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">[root@node232 ~]# rabbitmqctl cluster_status</span><br><span class="line">Cluster status of node rabbit@node232 ...</span><br><span class="line">Basics</span><br><span class="line"></span><br><span class="line">Cluster name: rabbit@node231</span><br><span class="line"></span><br><span class="line">Disk Nodes</span><br><span class="line"></span><br><span class="line">rabbit@node231</span><br><span class="line">rabbit@node232</span><br><span class="line">rabbit@node233</span><br><span class="line"></span><br><span class="line">Running Nodes</span><br><span class="line"></span><br><span class="line">rabbit@node231</span><br><span class="line">rabbit@node232</span><br><span class="line">rabbit@node233</span><br><span class="line"></span><br><span class="line">Versions</span><br><span class="line"></span><br><span class="line">rabbit@node231: RabbitMQ 3.8.1 on Erlang 22.2.1</span><br><span class="line">rabbit@node232: RabbitMQ 3.8.1 on Erlang 22.2.1</span><br><span class="line">rabbit@node233: RabbitMQ 3.8.1 on Erlang 23.2.3</span><br><span class="line"></span><br><span class="line">Alarms</span><br><span class="line"></span><br><span class="line">(none)</span><br><span class="line"></span><br><span class="line">Network Partitions</span><br><span class="line"></span><br><span class="line">(none)</span><br><span class="line"></span><br><span class="line">Listeners</span><br><span class="line"></span><br><span class="line">Node: rabbit@node231, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication</span><br><span class="line">Node: rabbit@node231, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0</span><br><span class="line">Node: rabbit@node231, interface: [::], port: 15672, protocol: http, purpose: HTTP API</span><br><span class="line">Node: rabbit@node232, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication</span><br><span class="line">Node: rabbit@node232, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0</span><br><span class="line">Node: rabbit@node232, interface: [::], port: 15672, protocol: http, purpose: HTTP API</span><br><span class="line">Node: rabbit@node233, interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication</span><br><span class="line">Node: rabbit@node233, interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0</span><br><span class="line">Node: rabbit@node233, interface: [::], port: 15672, protocol: http, purpose: HTTP API</span><br><span class="line"></span><br><span class="line">Feature flags</span><br><span class="line"></span><br><span class="line">Flag: drop_unroutable_metric, state: enabled</span><br><span class="line">Flag: empty_basic_get_metric, state: enabled</span><br><span class="line">Flag: implicit_default_bindings, state: enabled</span><br><span class="line">Flag: quorum_queue, state: enabled</span><br><span class="line">Flag: virtual_host_metadata, state: enabled</span><br></pre></td></tr></table></figure></li></ol><h3 id="gzip-is-not-a-supported-algorithm">2. 'gzip' is not a supported algorithm</h3><p><strong>问题描述：</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[root@dev235 ~]# haproxy -f /etc/haproxy/haproxy.cfg</span><br><span class="line">[ALERT] 103/185121 (15714) : parsing [/etc/haproxy/haproxy.cfg:9] : &#x27;compression&#x27; : &#x27;gzip&#x27; is not a supported algorithm.</span><br><span class="line"></span><br><span class="line">[ALERT] 103/185121 (15714) : Error(s) found in configuration file : /etc/haproxy/haproxy.cfg</span><br><span class="line">[ALERT] 103/185121 (15714) : Fatal errors found in configuration.</span><br></pre></td></tr></table></figure><p><strong>问题原因：</strong></p><p><a href="https://github.com/mesosphere/marathon-lb/issues/204">Add zlib support in haproxy for compression offloading</a></p><p><strong>解决方法：</strong></p><p>This lies in script <code>build-haproxy.sh</code>, where adding <code>USE_ZLIB=1</code> in the make stanza would do it.</p><p>重新编译安装</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">make TARGET=linux310 ARCH=x86_64 PREFIX=/usr/local/haproxy USE_ZLIB=1</span><br><span class="line">make install PREFIX=/usr/local/haproxy</span><br></pre></td></tr></table></figure><h2 id="references">References</h2><p><a href="https://www.rabbitmq.com/clustering.html">https://www.rabbitmq.com/clustering.html</a></p><p><a href="https://www.rabbitmq.com/ha.html">https://www.rabbitmq.com/ha.html</a></p><p><a href="https://www.rabbitmq.com/parameters.html#policies">https://www.rabbitmq.com/parameters.html#policies</a></p><p><a href="http://www.haproxy.org/">http://www.haproxy.org/</a></p><p><a href="https://cloud.tencent.com/developer/article/1158754">rabbitmq无法重新加入集群，启动失败的问题</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;RabbitMQ + HAProxy 高可用镜像模式集群部署&lt;/p&gt;
&lt;p&gt;本文介绍了如何搭建 RabbitMQ 高可用集群。首先介绍了部署说明和前提条件，然后详细介绍了集群模式和节点类型。接着介绍了环境准备和集群搭建的步骤，包括普通模式和镜像模式的配置。然后介绍了集群节点管理和故障处理的方法。最后介绍了使用 HAProxy 负载均衡的方法，并提供了安装、配置和验证的步骤。最后列举了一些常见问题。&lt;/p&gt;</summary>
    
    
    
    <category term="middleware" scheme="https://www.wylu.me/categories/middleware/"/>
    
    <category term="mq" scheme="https://www.wylu.me/categories/middleware/mq/"/>
    
    <category term="RabbitMQ" scheme="https://www.wylu.me/categories/middleware/mq/RabbitMQ/"/>
    
    
    <category term="rabbitmq" scheme="https://www.wylu.me/tags/rabbitmq/"/>
    
    <category term="haproxy" scheme="https://www.wylu.me/tags/haproxy/"/>
    
  </entry>
  
  <entry>
    <title>算法设计与分析</title>
    <link href="https://www.wylu.me/posts/3274548c/"/>
    <id>https://www.wylu.me/posts/3274548c/</id>
    <published>2020-05-17T10:18:26.000Z</published>
    <updated>2023-04-08T13:31:09.753Z</updated>
    
    <content type="html"><![CDATA[<p>本文主要介绍了算法的一些特性和常见算法的实现方法。其中，时间复杂度是算法效率的重要指标，递归是一种常见的算法实现方式，分治法、动态规划、贪心算法和回溯法是常见的算法设计思想。此外，还介绍了哈夫曼编码、单源最短路径、最小生成树、并查集和优先队列等常见算法的实现方法和应用场景。这些算法在计算机科学和工程领域中都有广泛的应用，对于提高算法效率和解决实际问题具有重要意义。</p><span id="more"></span><h1 id="算法设计与分析">算法设计与分析</h1><h2 id="算法的特性">算法的特性</h2><h3 id="定义">定义</h3><p>为了解决某类问题而规定的一个有限长的操作序列。</p><h3 id="算法的特性-1">算法的特性</h3><p>有穷性，确定性，可行性，输入，输出</p><h3 id="算法的性能标准">算法的性能标准</h3><p>正确性、可读性、健壮性、高效率和低存储需求</p><h2 id="时间复杂度">时间复杂度</h2><h3 id="常见算法时间复杂度">常见算法时间复杂度</h3><ul><li><span class="math inline">\(O(1)\)</span>: 表示算法的运行时间为常量</li><li><span class="math inline">\(O(n)\)</span>: 表示该算法是线性算法</li><li><span class="math inline">\(O(logn)\)</span>: 二分查找算法</li><li><span class="math inline">\(O(n^2)\)</span>: 对数组进行排序的简单算法，如直接插入排序。</li><li><span class="math inline">\(O(n^3)\)</span>: 做两个n阶矩阵的乘法运算</li><li><span class="math inline">\(O(2^n)\)</span>: 求具有n个元素集合的所有子集的算法</li><li><span class="math inline">\(O(n!)\)</span>: 求具有n个元素的全排列的算法</li></ul><h3 id="算法复杂性分析">算法复杂性分析</h3><p><span class="math display">\[f(n)=O(g(n)) \qquad f(n)的阶≤g(n)的阶\\f(n)=Ω(g(n)) \qquad f(n)的阶≥g(n)的阶\\f(n)=θ(g(n)) \qquad f(n)的阶＝g(n)的阶\\f(n)=o(g(n)) \qquad f(n)的阶＜g(n)的阶\\\]</span></p><h2 id="递归">递归</h2><h3 id="二分查找递归">二分查找（递归）</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">binary_search</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right, <span class="type">int</span> target)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(left &gt; right) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    <span class="type">int</span> mid = (left + right) / <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">if</span>(a[mid] == target) <span class="keyword">return</span> mid;</span><br><span class="line">    <span class="keyword">if</span>(a[mid] &lt; target) <span class="keyword">return</span> <span class="built_in">binary_search</span>(a, mid + <span class="number">1</span>, right, target);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">return</span> <span class="built_in">binary_search</span>(a, left, mid - <span class="number">1</span>, target);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="二分查找非递归">二分查找（非递归）</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">binary_search</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right, <span class="type">int</span> target)</span></span>&#123;</span><br><span class="line">    <span class="keyword">while</span>(left &lt;= right)&#123;</span><br><span class="line">        <span class="type">int</span> mid = (left + right) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span>(a[mid] == target) <span class="keyword">return</span> mid;</span><br><span class="line">        <span class="keyword">if</span>(a[mid] &lt; target) left = mid + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">else</span> right = mid - <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="快速排序">快速排序</h3><ul><li>选择划分基准 a(p)</li><li>将数组 a 划分成两个子数组，使得 <code>a[l...p-1] &lt;= a(p)</code>，<code>a[p+1...r] &gt;= a(p)</code></li><li>递归调用快速排序算法，对 <code>a[l...p-1]</code> 和 <code>a[p+1...r]</code> 进行排序</li></ul><h4 id="递归-1">递归</h4><p>C参考实现</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">quickSort</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right)</span>&#123;</span><br><span class="line">    <span class="type">int</span> i = left, j = right, base = a[left], tmp;</span><br><span class="line">    <span class="keyword">if</span>(left &gt;= right) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">while</span>(i != j)&#123;</span><br><span class="line">        <span class="keyword">while</span>(a[j] &gt;= base &amp;&amp; i &lt; j) j--;</span><br><span class="line">        <span class="keyword">while</span>(a[i] &lt;= base &amp;&amp; i &lt; j) i++;</span><br><span class="line">        <span class="keyword">if</span>(i &lt; j) tmp = a[i], a[i] = a[j], a[j] = tmp;</span><br><span class="line">    &#125;</span><br><span class="line">    a[left] = a[i], a[i] = base;</span><br><span class="line">    quickSort(a, left, i - <span class="number">1</span>);</span><br><span class="line">    quickSort(a, i + <span class="number">1</span>, right);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Java参考实现</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">QuickSort</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> i, <span class="type">int</span> j)</span>&#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">tmp</span> <span class="operator">=</span> arr[i];</span><br><span class="line">        arr[i] = arr[j];</span><br><span class="line">        arr[j] = tmp;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">partition</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> left, <span class="type">int</span> right)</span>&#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> left - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> left; i &lt; right; i++)</span><br><span class="line">            <span class="keyword">if</span> (arr[i] &lt; arr[right]) swap(arr, i, ++j);</span><br><span class="line">        swap(arr, right, ++j);</span><br><span class="line">        <span class="keyword">return</span> j;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> left, <span class="type">int</span> right)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(left &lt; right)&#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> partition(arr, left, right);</span><br><span class="line">            sort(arr, left, index - <span class="number">1</span>);</span><br><span class="line">            sort(arr, index + <span class="number">1</span>, right);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line">        <span class="type">int</span>[] arr = &#123;<span class="number">4</span>, <span class="number">8</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">9</span>, <span class="number">3</span>&#125;;</span><br><span class="line">        sort(arr, <span class="number">0</span>, arr.length - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> x: arr) System.out.print(x + <span class="string">&quot; &quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="非递归">非递归</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.LinkedList;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">QuickSort2</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> i, <span class="type">int</span> j)</span>&#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">tmp</span> <span class="operator">=</span> arr[i];</span><br><span class="line">        arr[i] = arr[j];</span><br><span class="line">        arr[j] = tmp;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">partition</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> left, <span class="type">int</span> right)</span>&#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> left - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> left; i &lt; right; i++)</span><br><span class="line">            <span class="keyword">if</span>(arr[i] &lt; arr[right]) swap(arr, i, ++j);</span><br><span class="line">        swap(arr, right, ++j);</span><br><span class="line">        <span class="keyword">return</span> j;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> left, <span class="type">int</span> right)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (left &gt;= right) <span class="keyword">return</span>;</span><br><span class="line">        LinkedList&lt;Integer&gt; stack = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">        stack.push(left);</span><br><span class="line">        stack.push(right);</span><br><span class="line">        <span class="keyword">while</span> (!stack.isEmpty())&#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">            <span class="type">int</span> <span class="variable">begin</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">            <span class="keyword">if</span> (begin &lt; end)&#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> partition(arr, begin, end);</span><br><span class="line">                stack.push(begin);</span><br><span class="line">                stack.push(index - <span class="number">1</span>);</span><br><span class="line">                stack.push(index + <span class="number">1</span>);</span><br><span class="line">                stack.push(end);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line">        <span class="type">int</span>[] arr = <span class="keyword">new</span> <span class="title class_">int</span>[]&#123;<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">9</span>, <span class="number">6</span>, <span class="number">0</span>, <span class="number">7</span>, <span class="number">2</span>, <span class="number">5</span>, <span class="number">8</span>&#125;;</span><br><span class="line">        sort(arr, <span class="number">0</span>, arr.length - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> e: arr) System.out.print(e + <span class="string">&quot; &quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="分治法">分治法</h2><h3 id="基本思想">基本思想</h3><p>将求解的较大规模的问题分割成 k 个更小规模的子问题。对这 k 个子问题分别求解。如果子问题的规模仍然不够小，则再划分为 k 个子问题，如此递归的进行下去，直到问题规模足够小，很容易求出其解为止。将求出的小规模的问题的解合并为一个更大规模的问题的解，自底向上逐步求出原来问题的解。</p><h3 id="适用条件">适用条件</h3><p>分治法所能解决的问题一般具有以下几个特征：</p><ul><li>该问题的规模缩小到一定的程度就可以容易地解决；</li><li>该问题可以分解为若干个规模较小的相同问题，即该问题具有最优子结构性质</li><li>利用该问题分解出的子问题的解可以合并为该问题的解；</li><li>该问题所分解出的各个子问题是相互独立的，即子问题之间不包含公共的子问题。</li></ul><h3 id="归并排序">归并排序</h3><p>其基本思想是：将待排序元素分成大小大致相同的 2 个子集合，分别对 2 个子集合进行排序，最终将排好序的子集合合并成为所要求的排好序的集合。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">merge</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> x, <span class="type">int</span> mid, <span class="type">int</span> y, <span class="type">int</span>* tmp)</span>&#123;</span><br><span class="line">    <span class="type">int</span> i = x, j = mid + <span class="number">1</span>, t = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(i &lt;= mid &amp;&amp; j &lt;= y) tmp[t++] = a[i] &lt;= a[j] ? a[i++] : a[j++];</span><br><span class="line">    <span class="keyword">while</span>(i &lt;= mid) tmp[t++] = a[i++];</span><br><span class="line">    <span class="keyword">while</span>(j &lt;= y) tmp[t++] = a[j++];</span><br><span class="line">    t = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(x &lt;= y) a[x++] = tmp[t++];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">sort</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> x, <span class="type">int</span> y, <span class="type">int</span>* tmp)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(x &lt; y)&#123;</span><br><span class="line">        <span class="type">int</span> mid = (x + y) / <span class="number">2</span>;</span><br><span class="line">        sort(a, x, mid, tmp);</span><br><span class="line">        sort(a, mid + <span class="number">1</span>, y, tmp);</span><br><span class="line">        merge(a, x, mid, y, tmp);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="求逆序对数">求逆序对数</h3><p>考虑 <span class="math inline">\(1,2,…,n\)</span> 的排列 <span class="math inline">\(i1，i2，…，in\)</span>，如果其中存在 <span class="math inline">\(j,k\)</span>，满足 <span class="math inline">\(j &lt; k\)</span> 且 <span class="math inline">\(i_j &gt; i_k\)</span>， 那么就称 <span class="math inline">\((i_j,i_k)\)</span> 是这个排列的一个逆序。</p><p>一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个逆序 (2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1)，因此该排列的逆序数就是 8。显然，由 1,2,…,n 构成的所有 n! 个排列中，最小的逆序数是 0，对应的排列就是 1,2,…,n；最大的逆序数是 n(n-1)/2，对应的排列就是 n,(n-1),…,2,1。逆序数越大的排列与原始排列的差异度就越大。</p><p>基本思路：</p><p>1.使用二分归并（分治法）进行求解； 2.将序列依此划分为两两相等的子序列； 3.对每个子序列进行排序（比较 <code>a[i] &gt; a[j]</code>，如果满足条件，则求该子序列的逆序数 <code>count = mid - i + 1</code>，其中 <code>mid = (left + right) / 2</code>） 4.接着合并子序列即可。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">merge</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> mid, <span class="type">int</span> right, <span class="type">int</span>* tmp)</span>&#123;</span><br><span class="line">    <span class="type">int</span> i = left, j = mid + <span class="number">1</span>, t = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(i &lt;= mid &amp;&amp; j &lt;= right)&#123;</span><br><span class="line">        <span class="keyword">if</span>(a[i] &lt;= a[j]) tmp[t++] = a[i++];</span><br><span class="line">        <span class="keyword">else</span>&#123;</span><br><span class="line">            tmp[t++] = a[j++];</span><br><span class="line">            count += mid - i + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span>(i &lt;= mid) tmp[t++] = a[i++];</span><br><span class="line">    <span class="keyword">while</span>(j &lt;= right) tmp[t++] = a[j++];</span><br><span class="line">    t = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(left &lt;= right) a[left++] = tmp[t++];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">mergeSort</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right, <span class="type">int</span>* tmp)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(left &lt; right)&#123;</span><br><span class="line">        <span class="type">int</span> mid = (left + right) / <span class="number">2</span>;</span><br><span class="line">        mergeSort(a, left, mid, tmp);</span><br><span class="line">        mergeSort(a, mid + <span class="number">1</span>, right, tmp);</span><br><span class="line">        merge(a, left, mid, right, tmp);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="快速选择算法">快速选择算法</h3><p>基本思想：</p><p>快速选择的总体思路与快速排序一致，选择一个元素作为基准来对元素进行分区，将小于和大于基准的元素分在基准左边和右边的两个区域。不同的是，快速选择并不递归访问双边，而是只递归进入一边的元素中继续寻找。这降低了平均时间复杂度，从 <span class="math inline">\(O(nlogn)\)</span> 至 <span class="math inline">\(O(n)\)</span>，不过最坏情况仍然是 <span class="math inline">\(O(n^2)\)</span>。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">partition</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right)</span>&#123;</span><br><span class="line">    <span class="type">int</span> j = left - <span class="number">1</span>, tmp;  <span class="comment">// 选择a[right]作为划分基准</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = left; i &lt; right; i++)&#123;</span><br><span class="line">        <span class="keyword">if</span>(a[i] &lt;= a[right]) tmp = a[i], a[i] = a[++j], a[j] = tmp;</span><br><span class="line">    &#125;</span><br><span class="line">    tmp = a[right], a[right] = a[++j], a[j] = tmp;</span><br><span class="line">    <span class="keyword">return</span> j;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">quick_select</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right, <span class="type">int</span> k)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(left == right) <span class="keyword">return</span> a[left];</span><br><span class="line">    <span class="type">int</span> idx = partition(a, left, right), cur = idx - left + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span>(k == cur) <span class="keyword">return</span> a[idx];</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(k &lt; cur) <span class="keyword">return</span> quick_select(a, left, idx - <span class="number">1</span>, k);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">return</span> quick_select(a, idx + <span class="number">1</span>, right, k - cur);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="线性时间选择bfprt">线性时间选择（BFPRT）</h3><p>基本思路：</p><ul><li>首先把数组按 5 个数为一组进行分组，最后不足 5 个的忽略。对每组数进行排序（如插入排序）求取其中位数。</li><li>把上一步的所有中位数移到数组的前面，对这些中位数递归调用 BFPRT 算法求得他们的中位数。</li><li>将上一步得到的中位数作为划分的主元进行整个数组的划分。</li><li>判断第k个数在划分结果的左边、右边还是恰好是划分结果本身，前两者递归处理，后者直接返回答案。</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">bubble_sort</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right)</span>&#123;</span><br><span class="line">    <span class="type">int</span> tmp;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = left; i &lt; right; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j = right; j &gt; i; j--)&#123;</span><br><span class="line">            <span class="keyword">if</span>(a[j] &lt; a[j<span class="number">-1</span>]) tmp = a[j], a[j] = a[j<span class="number">-1</span>], a[j<span class="number">-1</span>] = tmp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">partition</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right, <span class="type">int</span> baseIdx)</span>&#123;</span><br><span class="line">    <span class="type">int</span> j = left - <span class="number">1</span>, tmp;</span><br><span class="line">    <span class="comment">// 将基准放于数组尾部</span></span><br><span class="line">    tmp = a[right], a[right] = a[baseIdx], a[baseIdx] = tmp;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = left; i &lt; right; i++)&#123;</span><br><span class="line">        <span class="keyword">if</span>(a[i] &lt;= a[right]) tmp = a[i], a[i] = a[++j], a[j] = tmp;</span><br><span class="line">    &#125;</span><br><span class="line">    tmp = a[right], a[right] = a[++j], a[j] = tmp;</span><br><span class="line">    <span class="keyword">return</span> j;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">bfprt</span><span class="params">(<span class="type">int</span>* a, <span class="type">int</span> left, <span class="type">int</span> right, <span class="type">int</span> k)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(right - left + <span class="number">1</span> &lt;= <span class="number">5</span>)&#123;  <span class="comment">// 小于等于5个数，直接排序得到结果</span></span><br><span class="line">        bubble_sort(a, left, right);</span><br><span class="line">        <span class="keyword">return</span> a[left + k - <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> t = left - <span class="number">1</span>, tmp;  <span class="comment">// t:当前替换到前面的中位数的下标</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> st = left, ed; (ed = st + <span class="number">4</span>) &lt;= right; st += <span class="number">5</span>)&#123;</span><br><span class="line">        bubble_sort(a, st, ed);</span><br><span class="line">        <span class="comment">// 将中位数替换到数组前面，便于递归求取中位数的中位数</span></span><br><span class="line">        tmp = a[++t], a[t] = a[st+<span class="number">2</span>], a[st+<span class="number">2</span>] = tmp;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> baseIdx = (left + t) &gt;&gt; <span class="number">1</span>; <span class="comment">// left到t的中位数的下标，作为主元的下标</span></span><br><span class="line">    bfprt(a, left, t, baseIdx - left + <span class="number">1</span>); <span class="comment">// 不关心中位数的值，保证中位数在正确的位置</span></span><br><span class="line">    <span class="type">int</span> idx = partition(a, left, right, baseIdx), cur = idx - left + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span>(k == cur) <span class="keyword">return</span> a[idx];</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(k &lt; cur) <span class="keyword">return</span> bfprt(a, left, idx - <span class="number">1</span>, k);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">return</span> bfprt(a, idx + <span class="number">1</span>, right, k - cur);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="动态规划">动态规划</h2><h3 id="基本步骤">基本步骤</h3><ul><li>找出最优解的性质，并刻划其结构特征。</li><li>递归地定义最优值。</li><li>以自底向上的方式计算出最优值。</li><li>根据计算最优值时得到的信息，构造最优解。</li></ul><h3 id="矩阵连乘">矩阵连乘</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">matrixChain</span><span class="params">(<span class="type">int</span>[] p, <span class="type">int</span>[][] m, <span class="type">int</span>[][] s)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> p.length - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= n; i++) m[i][i] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> <span class="number">2</span>; r &lt;= n; r++)</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt;= n - r + <span class="number">1</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i + r - <span class="number">1</span>;</span><br><span class="line">            m[i][j] = m[i + <span class="number">1</span>][j] + p[i - <span class="number">1</span>] * p[i] * p[j];</span><br><span class="line">            s[i][j] = i;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> i + <span class="number">1</span>; k &lt; j; k++) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">t</span> <span class="operator">=</span> m[i][k] + m[k + <span class="number">1</span>][j] + p[i - <span class="number">1</span>] * p[k] * p[j];</span><br><span class="line">                <span class="keyword">if</span> (t &lt; m[i][j]) &#123;</span><br><span class="line">                    m[i][j] = t;</span><br><span class="line">                    s[i][j] = k;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="最优子结构">最优子结构</h3><p>当问题的最优解包含了其子问题的最优解时，称该问题具有最优子结构性质。</p><p>例如，矩阵连乘计算次序问题的最优解包含着其子问题的最优解，这种性质称为最优子结构性质。</p><p><strong>利用问题的最优子结构性质，以自底向上的方式递归地从子问题的最优解逐步构造出整个问题的最优解。最优子结构是问题能用动态规划算法求解的前提。</strong></p><h3 id="重叠子问题">重叠子问题</h3><p>在递归算法自顶向下求解问题时，每次产生的子问题并不总是新问题，有些子问题被反复计算多次。这种性质称为子问题的重叠性质。</p><p>动态规划算法，对每一个子问题只解一次，而后将其解保存在一个表格中，当再次需要解此子问题时，只是简单地用常数时间查看一下结果。</p><h3 id="lis最长单调递增子序列">LIS最长单调递增子序列</h3><p>设序列为 a[0:n-1]，记 b[i]：以 a[i] 为结尾元素的最长递增子序列的长度。</p><p>则序列 a 的最长递增子序列长度为：<code>max&#123;b[i]&#125;, 0&lt;=i&lt;n</code></p><p>如何求 b[i] ?</p><p><code>b[0] = 1</code>，<code>b[i] = max&#123;b[k]&#125; + 1, (0&lt;=k&lt;i, a[k]&lt;=a[i])</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">LIS</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>, b[<span class="number">0</span>] = <span class="number">1</span>; i &lt; n; i++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>, k = <span class="number">0</span>; j &lt; i; j++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(a[j] &lt;= a[i] &amp;&amp; k &lt; b[j]) k = b[j];</span><br><span class="line">        &#125;</span><br><span class="line">        b[i] = k + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="lcs最长公共子序列">LCS最长公共子序列</h3><p>给定 2 个序列，<span class="math inline">\(X={x_1,x_2,…,x_m}\)</span> 和 <span class="math inline">\(Y={y_1,y_2,…,y_n}\)</span>，找出 X 和 Y 的最长公共子序列。</p><p>设序列 <span class="math inline">\(X={x_1,x_2,…,x_m}\)</span> 和 <span class="math inline">\(Y={y_1,y_2,…,y_n}\)</span> 的最长公共子序列为 <span class="math inline">\(Z={z_1,z_2,…,z_k}\)</span>，则</p><p>1)若 <span class="math inline">\(x_m=y_n\)</span>，则 <span class="math inline">\(z_k=x_m=y_n\)</span>，且 <span class="math inline">\(Z_{k-1}\)</span> 是 <span class="math inline">\(X_{m-1}\)</span> 和 <span class="math inline">\(Y_{n-1}\)</span> 的最长公共子序列。</p><p>2)若 <span class="math inline">\(x_m≠y_n\)</span>，则 <span class="math inline">\(Z\)</span> 是 <span class="math inline">\(X_{m-1}\)</span> 和 <span class="math inline">\(Y\)</span> 的最长公共子序列，<span class="math inline">\(X\)</span> 和 <span class="math inline">\(Y_{n-1}\)</span> 的最长公共子序列，中较长的序列。</p><p><strong>2 个序列的最长公共子序列包含了这 2 个序列的前缀的最长公共子序列。因此，最长公共子序列问题具有最优子结构性质。</strong></p><p>由最长公共子序列问题的最优子结构性质建立子问题最优值的递归关系。用 <span class="math inline">\(c[i][j]\)</span> 记录序列的最长公共子序列的长度。其中，<span class="math inline">\(X_i={x_1,x_2,…,x_i}\)</span>；<span class="math inline">\(Y_j={y_1,y_2,…,y_j}\)</span>。当 <span class="math inline">\(i=0\)</span> 或 <span class="math inline">\(j=0\)</span> 时，空序列是 <span class="math inline">\(X_i\)</span> 和 <span class="math inline">\(Y_j\)</span> 的最长公共子序列。故此时 <span class="math inline">\(c[i][j]=0\)</span>。其他情况下，由最优子结构性质可建立递归关系如下：</p><ul><li><code>i=0,j=0</code>：<code>c[i][j]=0</code></li><li><code>i,j&gt;0; x[i]==y[i]</code>：<code>c[i][j]=c[i-1][j-1]+1</code></li><li><code>i,j&gt;0; x[i]!=y[i]</code>：<code>c[i][j]=max&#123;c[i][j-1],c[i-1][j]&#125;</code></li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">memset</span>(dp, <span class="number">0</span>, <span class="keyword">sizeof</span>(dp));</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="built_in">strlen</span>(a); ++i) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; <span class="built_in">strlen</span>(b); ++j) &#123;</span><br><span class="line">        <span class="keyword">if</span>(a[i] == b[j]) dp[i+<span class="number">1</span>][j+<span class="number">1</span>] = dp[i][j] + <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">else</span> dp[i+<span class="number">1</span>][j+<span class="number">1</span>] = max(dp[i+<span class="number">1</span>][j], dp[i][j+<span class="number">1</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Algorithm <span class="title">lcsLength</span><span class="params">(x, y)</span></span>&#123;</span><br><span class="line">    m = x.length - <span class="number">1</span>;</span><br><span class="line">    n = y.length - <span class="number">1</span>;</span><br><span class="line">    c[i][<span class="number">0</span>] = <span class="number">0</span>; c[<span class="number">0</span>][i] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= m; i++)</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= n; j++)</span><br><span class="line">            <span class="keyword">if</span> (x[i] == y[j]) &#123;</span><br><span class="line">                c[i][j] = c[i<span class="number">-1</span>][j<span class="number">-1</span>] + <span class="number">1</span>;</span><br><span class="line">            &#125;<span class="keyword">else</span> <span class="keyword">if</span> (c[i<span class="number">-1</span>][j] &gt;= c[i][j<span class="number">-1</span>]) &#123;</span><br><span class="line">                c[i][j] = c[i<span class="number">-1</span>][j];</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                c[i][j] = c[i][j<span class="number">-1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">Algorithm <span class="title">lcs</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j, <span class="type">char</span> [] x)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (i == <span class="number">0</span> || j == <span class="number">0</span>) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">if</span> (x[i] == y[i])&#123;</span><br><span class="line">        <span class="built_in">lcs</span>(i<span class="number">-1</span>, j<span class="number">-1</span>, x);</span><br><span class="line">        <span class="built_in">print</span>(x[i]);</span><br><span class="line">    &#125;<span class="keyword">else</span> <span class="keyword">if</span> (c[i<span class="number">-1</span>][j] &gt;= c[i][j<span class="number">-1</span>]) <span class="built_in">lcs</span>(i<span class="number">-1</span>, j, x);</span><br><span class="line">    <span class="keyword">else</span> <span class="built_in">lcs</span>(i, j<span class="number">-1</span>, x);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="分治法与动态规划的区别">分治法与动态规划的区别</h3><ul><li>分治法与动态规划有何共同点？提示：从所适用问题的特点，解决问题的方式上阐述<ul><li>两者所解决的问题，都能划分为若干个规模较小的子问题</li><li>这些子问题具有最优子结构性质</li><li>能通过子问题的最优解自底向上地得到问题的最优解</li></ul></li><li>分治法与动态规划又有何不同？提示：从子问题的角度阐述<ul><li>分治法适用于子问题相互独立的情况，即子问题之间不存在公共子问题</li><li>动态规划适用于子问题存在重叠的情况，对每一个子问题只解一次，而后将其解保存在一个表格中，当再次需要解此子问题时，只是简单地用常数时间查看一下结果</li></ul></li></ul><h2 id="贪心算法">贪心算法</h2><h3 id="基本思想-1">基本思想</h3><p>贪心算法总是做出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑，它所作出的选择只是在某种意义上的局部最优选择。在一些情况下，即使贪心算法不能得到整体最优解，其最终结果却是最优解的很好近似。</p><h3 id="活动安排问题">活动安排问题</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 各活动的起始时间和结束时间存储于数组s和f中且按结束时间的非减序排列</span></span><br><span class="line"><span class="comment"> * a数组记录是否安排相应活动</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">greedySelector</span><span class="params">(<span class="type">int</span> [] s, <span class="type">int</span> [] f, <span class="type">boolean</span> a[])</span>&#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> s.length-<span class="number">1</span>;</span><br><span class="line">    a[<span class="number">1</span>] = <span class="literal">true</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (s[i] &gt;= f[j]) &#123;</span><br><span class="line">            a[i] = <span class="literal">true</span>;</span><br><span class="line">            j = i;</span><br><span class="line">            count++;</span><br><span class="line">        &#125;<span class="keyword">else</span> a[i] = <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="最优子结构性质">最优子结构性质</h3><p>当一个问题的最优解包含其子问题的最优解时，称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。</p><h3 id="贪心选择性质">贪心选择性质</h3><p>所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择，即贪心选择来达到。这是贪心算法可行的第一个基本要素，也是贪心算法与动态规划算法的主要区别。</p><p>在动态规划算法中，每步所做出的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后，才能做出选择。而在贪心算法中，仅在当前状态下做出最好选择，即局部最优选择。然后再去解做出这个选择后产生的相应的子问题。</p><p><strong>贪心算法和动态规划算法都要求问题具有最优子结构性质，这是两类算法的一个共同点。</strong></p><p>对于一个具体问题，要确定它是否具有贪心选择性质，必须证明每一步所作的贪心选择最终导致问题的整体最优解。（即证明有解必有贪心解）</p><h3 id="贪心算法求解背包问题">贪心算法求解背包问题</h3><p>基本步骤：</p><ul><li>首先计算每种物品单位重量的价值 <span class="math inline">\(V_i/W_i\)</span></li><li>然后，依贪心选择策略，将尽可能多的单位重量价值最高的物品装入背包</li><li>若将这种物品全部装入背包后，背包内的物品总重量未超过 C，则选择单位重量价值次高的物品并尽可能多地装入背包。</li><li>依此策略一直地进行下去，直到背包装满为止。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">float</span> <span class="title function_">knapsack</span><span class="params">(<span class="type">float</span> c, <span class="type">float</span>[] w, <span class="type">float</span>[] v,<span class="type">float</span>[] x)</span>&#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> v.length;</span><br><span class="line">    Element [] d = <span class="keyword">new</span> <span class="title class_">Element</span> [n];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; i++) d[i] = <span class="keyword">new</span> <span class="title class_">Element</span>(w[i], v[i], i);</span><br><span class="line">    MergeSort.mergeSort(d);</span><br><span class="line">    <span class="type">int</span> i;</span><br><span class="line">    <span class="type">float</span> <span class="variable">opt</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; n; i++) x[i] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (d[i].w &gt; c) <span class="keyword">break</span>;</span><br><span class="line">        x[d[i].i] = <span class="number">1</span>;</span><br><span class="line">        opt += d[i].v;</span><br><span class="line">        c -= d[i].w;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (i &lt; n)&#123;</span><br><span class="line">        x[d[i].i] = c / d[i].w;</span><br><span class="line">        opt += x[d[i].i] * d[i].v;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> opt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对于 0-1 背包问题，贪心选择之所以不能得到最优解是因为在这种情况下，它无法保证最终能将背包装满，部分闲置的背包空间使每公斤背包空间的价值降低了。</p><p>事实上，在考虑 0-1 背包问题时，应比较选择该物品和不选择该物品所导致的最终方案，然后再作出最好选择。</p><h3 id="最优装载问题">最优装载问题</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">float</span> <span class="title function_">loading</span><span class="params">(<span class="type">float</span> c, <span class="type">float</span>[] w, <span class="type">int</span>[] x)</span>&#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> w.length;</span><br><span class="line">    Element [] d = <span class="keyword">new</span> <span class="title class_">Element</span> [n];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; i++) d[i] = <span class="keyword">new</span> <span class="title class_">Element</span>(w[i], i);</span><br><span class="line">    MergeSort.mergeSort(d);</span><br><span class="line">    <span class="type">float</span> <span class="variable">opt</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; i++) x[i] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n &amp;&amp; d[i].w &lt;= c; i++) &#123;</span><br><span class="line">        x[d[i].i] = <span class="number">1</span>;</span><br><span class="line">        opt += d[i].w;</span><br><span class="line">        c -= d[i].w;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> opt;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="哈夫曼编码">哈夫曼编码</h2><h3 id="应用">应用</h3><p>哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。给出现频率高的字符较短的编码，出现频率较低的字符以较长的编码，可以大大缩短总码长。</p><h3 id="构造哈夫曼编码">构造哈夫曼编码</h3><ul><li>哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树 T。</li><li>算法以 C 个叶结点开始，执行 C-1 次的“合并”运算后产生最终所要求的树 T。</li></ul><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/891129703366840b1bac668d27b5cd11.png" alt="huffman-1" /></p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/af9941f332989278833bd160b2a58181.png" alt="huffman-2" /></p><h2 id="单源最短路径">单源最短路径</h2><h3 id="单源最短路问题">单源最短路问题</h3><p>给定带权有向图 G=(V,E)，其中每条边的权是非负实数。另外，还给定 V 中的一个顶点，称为源。现在要计算从源到所有其他各顶点的最短路长度。这里路的长度是指路上各边权之和。</p><h3 id="dijkstra-算法">Dijkstra 算法</h3><p>Dijkstra 算法是解单源最短路径问题的贪心算法。其基本思想是，设置顶点集合 S 并不断地作贪心选择来扩充这个集合。一个顶点属于集合 S 当且仅当从源到该顶点的最短路径长度已知。</p><p>基本步骤：</p><ul><li>初始时，S 中仅含有源。</li><li>设 u 是 G 的某一个顶点，把从源到 u 且中间只经过 S 中顶点的路称为从源到 u 的特殊路径，并用数组 dist 记录当前每个顶点所对应的最短特殊路径长度。</li><li>Dijkstra 算法每次从 V-S 中取出具有最短特殊路长度的顶点 u，将 u 添加到 S 中，同时对数组 dist 作必要的修改。</li><li>一旦 S 包含了所有 V 中顶点，dist 就记录了从源到所有其他顶点之间的最短路径长度。</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> cost[MAX_V][MAX_V];  <span class="comment">// cost[u][v]表示边e=(u,v)的权值（不存在这条边时设为INF）</span></span><br><span class="line"><span class="type">int</span> dist[MAX_V];  <span class="comment">// 顶点s出发的最短距离</span></span><br><span class="line"><span class="type">bool</span> used[MAX_V];  <span class="comment">// 已经使用过的图</span></span><br><span class="line"><span class="type">int</span> V;  <span class="comment">// 顶点数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 求从起点s出发到各个顶点的最短距离</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dijkstra</span><span class="params">(<span class="type">int</span> s)</span></span>&#123;</span><br><span class="line">    <span class="built_in">fill</span>(dist, dist + V, INF);</span><br><span class="line">    <span class="built_in">fill</span>(used, used + V, <span class="literal">false</span>);</span><br><span class="line">    dist[s] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">        <span class="type">int</span> v = <span class="number">-1</span>;</span><br><span class="line">        <span class="comment">// 从尚未使用过的顶点中选择一个距离最小的顶点</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> u = <span class="number">0</span>; u &lt; V; u++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(!used[u] &amp;&amp; (v == <span class="number">-1</span> || dist[u] &lt; dist[v])) v = u;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(v == <span class="number">-1</span>) <span class="keyword">break</span>;</span><br><span class="line">        used[v] = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> u = <span class="number">0</span>; u &lt; V; u++)&#123;</span><br><span class="line">            dist[u] = <span class="built_in">min</span>(dist[u], dist[v] + cost[v][u]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="最小生成树">最小生成树</h2><h3 id="定义-1">定义</h3><p>设 <span class="math inline">\(G =(V,E)\)</span> 是无向连通带权图，即一个网络。<span class="math inline">\(E\)</span> 中每条边 <span class="math inline">\((v,w)\)</span> 的权为 <span class="math inline">\(c[v][w]\)</span>。如果 <span class="math inline">\(G\)</span> 的子图 <span class="math inline">\(G’\)</span> 是一棵包含 <span class="math inline">\(G\)</span> 的所有顶点的树，则称 <span class="math inline">\(G’\)</span> 为 <span class="math inline">\(G\)</span> 的生成树。生成树上各边权的总和称为该生成树的耗费。在 <span class="math inline">\(G\)</span> 的所有生成树中，耗费最小的生成树称为 <span class="math inline">\(G\)</span> 的最小生成树。</p><h3 id="最小生成树性质">最小生成树性质</h3><p>设 <span class="math inline">\(G=(V,E)\)</span> 是连通带权图，<span class="math inline">\(U\)</span> 是 <span class="math inline">\(V\)</span> 的真子集。如果 <span class="math inline">\((u,v) \in E\)</span>，且 <span class="math inline">\(u \in U\)</span>，<span class="math inline">\(v \in V-U\)</span>，且在所有这样的边中，<span class="math inline">\((u,v)\)</span> 的权 <span class="math inline">\(c[u][v]\)</span> 最小，那么一定存在 <span class="math inline">\(G\)</span> 的一棵最小生成树，它以 <span class="math inline">\((u,v)\)</span> 为其中一条边。这个性质有时也称为 MST 性质。</p><h3 id="prim算法">Prim算法</h3><p>设 <span class="math inline">\(G=(V,E)\)</span> 是连通带权图，<span class="math inline">\(V={1,2,…,n}\)</span>。构造 <span class="math inline">\(G\)</span> 的最小生成树的 Prim 算法的基本思想是：</p><ul><li>首先置 <span class="math inline">\(S=\{1\}\)</span>，</li><li>然后，只要 <span class="math inline">\(S\)</span> 是 <span class="math inline">\(V\)</span> 的真子集，就作如下的贪心选择</li><li>选取满足条件 <span class="math inline">\(i \in S\)</span>，<span class="math inline">\(j \in V-S\)</span>，且 <span class="math inline">\(c[i][j]\)</span> 最小的边，将顶点j添加到 <span class="math inline">\(S\)</span> 中。</li><li>这个过程一直进行到 <span class="math inline">\(S=V\)</span> 时为止。在这个过程中选取到的所有边恰好构成 G 的一棵最小生成树。</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> cost[MAX_V][MAX_V];  <span class="comment">// cost[u][v]表示边e=(u,v)的权值（不存在这条边时设为INF）</span></span><br><span class="line"><span class="type">int</span> mistcost[MAX_V];  <span class="comment">// 从集合X出发的边到每个顶点的最小权值</span></span><br><span class="line"><span class="type">bool</span> used[MAX_V];  <span class="comment">// 顶点i是否包含在集合X中</span></span><br><span class="line"><span class="type">int</span> V;  <span class="comment">// 顶点数</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">prim</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; V; i++)&#123;</span><br><span class="line">        mincost[i] = INF;</span><br><span class="line">        used[i] = <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    mincost[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="type">int</span> v = <span class="number">-1</span>;</span><br><span class="line">        <span class="comment">// 从不属于X的顶点中选取从X到其权值最小的顶点</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> u = <span class="number">0</span>; u &lt; V; u++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(!used[u] &amp;&amp; (v == <span class="number">-1</span> || mincost[u] &lt; mincost[v])) v = u;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(v == <span class="number">-1</span>) <span class="keyword">break</span>;</span><br><span class="line">        used[v] = <span class="literal">true</span>;  <span class="comment">// 把顶点v加入X</span></span><br><span class="line">        res += mincost[v];  <span class="comment">// 把边的长度加到结果里</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> u = <span class="number">0</span>; u &lt; V; u++)&#123;</span><br><span class="line">            mincost[u] = <span class="built_in">min</span>(mincost[u], cost[v][u]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="kruskal-算法">Kruskal 算法</h3><p>Kruskal 算法构造 <span class="math inline">\(G\)</span> 的最小生成树的基本思想是：</p><ul><li>首先将 <span class="math inline">\(G\)</span> 的 <span class="math inline">\(n\)</span> 个顶点看成 <span class="math inline">\(n\)</span> 个孤立的连通分支。</li><li>将所有的边按权从小到大排序。</li><li>然后从第一条边开始，依边权递增的顺序查看每一条边，并按下述方法连接 2 个不同的连通分支</li><li>当查看到第 <span class="math inline">\(k\)</span> 条边 <span class="math inline">\((v,w)\)</span> 时，如果端点 <span class="math inline">\(v\)</span> 和 <span class="math inline">\(w\)</span> 分别是当前 2 个不同的连通分支 <span class="math inline">\(T1\)</span> 和 <span class="math inline">\(T2\)</span> 中的顶点时，就用边 <span class="math inline">\((v,w)\)</span> 将 <span class="math inline">\(T1\)</span> 和 <span class="math inline">\(T2\)</span> 连接成一个连通分支，然后继续查看第 <span class="math inline">\(k+1\)</span> 条边；</li><li>如果端点 <span class="math inline">\(v\)</span> 和 <span class="math inline">\(w\)</span> 在当前的同一个连通分支中，就直接再查看第 <span class="math inline">\(k+1\)</span> 条边。</li><li>这个过程一直进行到只剩下一个连通分支时为止。</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">edge</span> &#123;</span><br><span class="line">    <span class="type">int</span> u;</span><br><span class="line">    <span class="type">int</span> v;</span><br><span class="line">    <span class="type">int</span> cost;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmp</span><span class="params">(<span class="type">const</span> edge &amp;e1, <span class="type">const</span> edge &amp;e2)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> e1.cost &lt; e2.cost;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">edge es[MAX_E];</span><br><span class="line"><span class="type">int</span> V, E;  <span class="comment">// 顶点数和边数</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">krustral</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">sort</span>(es, es + E, comp);  <span class="comment">// 按照edge.cost的顺序从小到大排列</span></span><br><span class="line">    <span class="built_in">init_union_find</span>(V);  <span class="comment">// 并查集初始化</span></span><br><span class="line">    <span class="type">int</span> res = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; E; i++)&#123;</span><br><span class="line">        edge e = es[i];</span><br><span class="line">        <span class="keyword">if</span>(!<span class="built_in">same</span>(e.u, e.v))&#123;</span><br><span class="line">            <span class="built_in">unite</span>(e.u, e.v);</span><br><span class="line">            res += e.cost;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="并查集">并查集</h2><h3 id="主要操作">主要操作</h3><ul><li>初始化 集合中每个元素单独作为一个子集。</li><li>查找 查找元素 x 所在的子集序号。常用来判断元素 x 和 y 是否在同一子集中。</li><li>合并 将元素 x 和 y 分别所在的子集合并为一个子集。</li></ul><h3 id="算法实现">算法实现</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用编号代表每个元素，数组par表示父亲的编号，当par[x]=x时，x是所在树的树根</span></span><br><span class="line"><span class="type">int</span> par[MAX_N];  <span class="comment">// 父亲</span></span><br><span class="line"><span class="type">int</span> rank[MAX_N];  <span class="comment">// 树的高度</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化n个元素</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        par[i] = i;</span><br><span class="line">        rank[i] = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 查询树的根</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">find</span><span class="params">(<span class="type">int</span> x)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(par[x] == x) &#123;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> par[x] = <span class="built_in">find</span>(par[x]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 合并x和y所属的集合</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">unite</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> </span>&#123;</span><br><span class="line">    x = <span class="built_in">find</span>(x);</span><br><span class="line">    y = <span class="built_in">find</span>(y);</span><br><span class="line">    <span class="keyword">if</span>(x == y) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">if</span>(rank[x] &lt; rank[y]) &#123;</span><br><span class="line">        par[x] = y;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        par[y] = x;</span><br><span class="line">        <span class="keyword">if</span>(rank[x] == rank[y]) rank[x]++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="优先队列">优先队列</h2><h3 id="主要操作-1">主要操作</h3><ul><li>初始化 将给定多个元素初始化为优先队列。</li><li>出队 将优先权最大的元素x出队，并调整结构为优先队列。</li><li>入队 加入元素x，并调整结构为优先队列。</li></ul><h3 id="算法实现-1">算法实现</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 节点从0开始编号</span></span><br><span class="line"><span class="type">int</span> heap[MAX_N], sz = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">push</span><span class="params">(<span class="type">int</span> x)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 自己节点的编号</span></span><br><span class="line">    <span class="type">int</span> i = sz++;</span><br><span class="line">    <span class="keyword">while</span>(i &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 父亲节点的编号</span></span><br><span class="line">        <span class="type">int</span> p = (i<span class="number">-1</span>) / <span class="number">2</span>;</span><br><span class="line">        <span class="comment">// 如果已经没有大小颠倒则退出</span></span><br><span class="line">        <span class="keyword">if</span>(heap[p] &lt;= x) <span class="keyword">break</span>;</span><br><span class="line">        <span class="comment">// 把父亲节点的数组放下来，而把自己提上去</span></span><br><span class="line">        heap[i] = heap[p];</span><br><span class="line">        i = p;</span><br><span class="line">    &#125;</span><br><span class="line">    heap[i] = x;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 最小值</span></span><br><span class="line">    <span class="type">int</span> ret = heap[<span class="number">0</span>];</span><br><span class="line">    <span class="comment">// 要提到根的数值</span></span><br><span class="line">    <span class="type">int</span> x = heap[--sz];</span><br><span class="line">    <span class="comment">// 从根开始向下交换</span></span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(i * <span class="number">2</span> + <span class="number">1</span> &lt; sz) &#123;</span><br><span class="line">        <span class="comment">// 比较儿子的值</span></span><br><span class="line">        <span class="type">int</span> a = i * <span class="number">2</span> + <span class="number">1</span>, b = i * <span class="number">2</span> + <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span>(b &lt; sz &amp;&amp; heap[b] &lt; heap[a]) a = b;</span><br><span class="line">        <span class="comment">// 如果已经没有大小颠倒则退出</span></span><br><span class="line">        <span class="keyword">if</span>(heap[a] &gt;= x) <span class="keyword">break</span>;</span><br><span class="line">        <span class="comment">// 把儿子的数值提上来</span></span><br><span class="line">        heap[i] = heap[a];</span><br><span class="line">        i = a;</span><br><span class="line">    &#125;</span><br><span class="line">    heap[i] = x;</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="回溯法">回溯法</h2><h3 id="定义-2">定义</h3><p>为了避免生成那些不可能产生最佳解的问题状态，要不断地利用限界函数(bounding function)来处死那些实际上不可能产生所需解的活结点，以减少问题的计算量。具有限界函数的深度优先生成法称为回溯法。</p><h3 id="基本思想-2">基本思想</h3><ul><li>针对所给问题，定义问题的解空间；</li><li>确定易于搜索的解空间结构；</li><li>以深度优先方式搜索解空间，并在搜索过程中用剪枝函数避免无效搜索。</li></ul><p><strong>常用剪枝函数：用约束函数在扩展结点处剪去不满足约束的子树；用限界函数剪去得不到最优解的子树。</strong> 这种方法适用于解一些组合数相当大的问题。</p><h3 id="回溯法的特征">回溯法的特征</h3><p>用回溯法解题的一个显著特征是在搜索过程中动态产生问题的解空间。在任何时刻，算法只保存从根结点到当前扩展结点的路径。如果解空间树中从根结点到叶结点的最长路径的长度为 <span class="math inline">\(h(n)\)</span>，则回溯法所需的计算空间通常为 <span class="math inline">\(O(h(n))\)</span>。</p><h3 id="递归回溯">递归回溯</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span><span class="params">(<span class="type">int</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (t &gt; n) <span class="built_in">output</span>(x);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="built_in">f</span>(n,t); i &lt;= <span class="built_in">g</span>(n,t); i++) &#123;</span><br><span class="line">            x[t] = <span class="built_in">h</span>(i);</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">constraint</span>(t) &amp;&amp; <span class="built_in">bound</span>(t)) <span class="built_in">backtrack</span>(t + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="迭代回溯">迭代回溯</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">iterativeBacktrack</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> t = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span> (t &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">f</span>(n,t) &lt;= <span class="built_in">g</span>(n,t))</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> i = <span class="built_in">f</span>(n,t); i &lt;= <span class="built_in">g</span>(n,t); i++) &#123;</span><br><span class="line">                x[t] = <span class="built_in">h</span>(i);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">constraint</span>(t) &amp;&amp; <span class="built_in">bound</span>(t)) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (<span class="built_in">solution</span>(t)) <span class="built_in">output</span>(x);</span><br><span class="line">                    <span class="keyword">else</span> t++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="keyword">else</span> t--;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="求s的所有元素个数小于4的子集">求S的所有元素个数小于4的子集</h3><p>已知集合 <span class="math inline">\(S=\{a,b,c,d,e,f,g\}\)</span>，请编程输出 <span class="math inline">\(S\)</span> 的所有元素个数小于 4 的子集。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> n 7</span></span><br><span class="line"><span class="type">char</span> s[n] = &#123;a,b,c,d,e,f,g&#125;;</span><br><span class="line"><span class="type">int</span> x[n+<span class="number">1</span>];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">int</span>* x)</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">all_subset</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">backtrack</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span> <span class="params">(<span class="type">int</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (t &gt;= n) <span class="built_in">output</span>(x);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt;= <span class="number">1</span>; i++) &#123;</span><br><span class="line">            x[t] = i;</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">count</span>(x, t) &lt; <span class="number">4</span>) <span class="built_in">backtrack</span>(t + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="求s的所有元素和小于8的子集">求S的所有元素和小于8的子集</h3><p>已知集合 <span class="math inline">\(S=\{1,2,3,4,5,6,7\}\)</span>，请编程输出 <span class="math inline">\(S\)</span> 的所有元素和小于 8 的子集。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> n 7</span></span><br><span class="line"><span class="type">char</span> s[n] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>&#125;;</span><br><span class="line"><span class="type">int</span> x[n+<span class="number">1</span>];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">int</span>* x)</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">all_subset</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">backtrack</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span> <span class="params">(<span class="type">int</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (t &gt;= n) <span class="built_in">output</span>(x);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt;= <span class="number">1</span>; i++) &#123;</span><br><span class="line">            x[t] = i;</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">sum</span>(x, t) &lt; <span class="number">8</span>) <span class="built_in">backtrack</span>(t + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="求s满足元素奇偶性相同且和小于8的子集">求S满足元素奇偶性相同且和小于8的子集</h3><p>已知集合 <span class="math inline">\(S=\{1,2,3,4,5,6,7\}\)</span>，请编程输出 <span class="math inline">\(S\)</span> 的所有满足下列条件的子集：元素奇偶性相同，且和小于 8。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> n 7</span></span><br><span class="line"><span class="type">char</span> s[n] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>&#125;;</span><br><span class="line"><span class="type">int</span> x[n+<span class="number">1</span>];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">int</span>* x)</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">all_subset</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">backtrack</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span> <span class="params">(<span class="type">int</span> t, <span class="type">int</span> sum, <span class="type">int</span> prior)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (t &gt;= n) <span class="built_in">output</span>(x);</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        x[t] = <span class="number">0</span>, <span class="built_in">backtrack</span>(t + <span class="number">1</span>, sum, prior);</span><br><span class="line">        x[t] = <span class="number">1</span>;</span><br><span class="line">        sum += s[t];</span><br><span class="line">        <span class="keyword">if</span>(sum &lt; <span class="number">8</span> &amp;&amp; (prior == <span class="number">-1</span> || (s[t] - s[prior]) % <span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="built_in">backtrack</span>(t + <span class="number">1</span>, sum, t);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="求s的所有排列">求S的所有排列</h3><p>已知集合 <span class="math inline">\(S=\{1,2,3,4,5,6,7\}\)</span>，请编程输出 S 的所有排列。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> n 7</span></span><br><span class="line"><span class="type">char</span> s[n+<span class="number">1</span>] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>&#125;;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">char</span>* s)</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">all_permutation</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">backtrack</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span> <span class="params">(<span class="type">int</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (t &gt;= n) <span class="built_in">output</span>(s);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = t; i &lt; n; i++) &#123;</span><br><span class="line">            <span class="built_in">swap</span>(s[t], s[i]);</span><br><span class="line">            <span class="built_in">backtrack</span>(t + <span class="number">1</span>);</span><br><span class="line">            <span class="built_in">swap</span>(s[t], s[i]);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="求s的所有满足奇偶数相间出现的排列">求S的所有满足奇偶数相间出现的排列</h3><p>已知集合 <span class="math inline">\(S=\{1,2,3,4,5,6,7,8\}\)</span>，请编程输出 S 的所有满足下列条件的排列：奇偶数相间出现。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> n 7</span></span><br><span class="line"><span class="type">char</span> s[n+<span class="number">1</span>] = &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>&#125;;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">char</span>* s)</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">all_permutation</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">backtrack</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span> <span class="params">(<span class="type">int</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (t &gt;= n) <span class="built_in">output</span>(s);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = t; i &lt; n; i++) &#123;</span><br><span class="line">            <span class="built_in">swap</span>(s[t], s[i]);</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">legal</span>(t)) <span class="built_in">backtrack</span>(t + <span class="number">1</span>);</span><br><span class="line">            <span class="built_in">swap</span>(s[t], s[i]);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">legal</span><span class="params">(<span class="type">int</span> t)</span></span>&#123;</span><br><span class="line">    <span class="type">bool</span> bRet = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">if</span>(t &gt; <span class="number">0</span>) bRet &amp;&amp;= ((s[t - <span class="number">1</span>] - s[t]) % <span class="number">2</span> == <span class="number">1</span>);</span><br><span class="line">    <span class="keyword">return</span> bRet;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="背包问题">0-1 背包问题</h3><p>重量 <span class="math inline">\(w=\{2,2,3,4,5,5,6\}\)</span>， 价值 <span class="math inline">\(v=\{3,4,3,4,5,8,7\}\)</span>，<span class="math inline">\(C=16\)</span>，求背包的最大价值。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> n 7</span></span><br><span class="line"><span class="type">int</span> C = <span class="number">16</span>;</span><br><span class="line"><span class="type">int</span> w[n] = &#123;<span class="number">2</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">5</span>,<span class="number">6</span>&#125;;</span><br><span class="line"><span class="type">int</span> v[n] = &#123;<span class="number">3</span>,<span class="number">4</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">8</span>,<span class="number">7</span>&#125;;</span><br><span class="line"><span class="type">int</span> x[n+<span class="number">1</span>], Max = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(<span class="type">int</span>* x)</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">backtrack</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span> <span class="params">(<span class="type">int</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (t &gt;= n) <span class="built_in">process</span>(x);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt;= <span class="number">1</span>; i++) &#123;</span><br><span class="line">            x[t] = i;</span><br><span class="line">            <span class="keyword">if</span>(<span class="built_in">legal</span>(t)) <span class="built_in">backtrack</span>(t + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">legal</span><span class="params">(<span class="type">int</span> t)</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> sum = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt;= t; i++)&#123;</span><br><span class="line">        sum += x[i] * w[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> sum &lt;= C;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">process</span><span class="params">(x)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++)&#123;</span><br><span class="line">        sum += x[i] * v[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(Max &lt; sum) Max = sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="装载问题">装载问题</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span> <span class="params">(<span class="type">int</span> i)</span> </span>&#123;  <span class="comment">// 搜索第i层结点</span></span><br><span class="line">    <span class="keyword">if</span> (i &gt; n) &#123;  <span class="comment">//到达叶结点更新最优解bestx</span></span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    r -= w[i];</span><br><span class="line">    <span class="keyword">if</span> (cw + w[i] &lt;= c) &#123;  <span class="comment">// 搜索左子树</span></span><br><span class="line">        x[i] = <span class="number">1</span>;</span><br><span class="line">        cw += w[i];</span><br><span class="line">        <span class="built_in">backtrack</span>(i + <span class="number">1</span>);</span><br><span class="line">        cw -= w[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (cw + r &gt; bestw)  &#123;  <span class="comment">// 搜索右子树</span></span><br><span class="line">        x[i] = <span class="number">0</span>;</span><br><span class="line">        <span class="built_in">backtrack</span>(i + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    r += w[i];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="n皇后问题">n皇后问题</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">boolean <span class="title">place</span><span class="params">(<span class="type">int</span> k)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt; k; j++)</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">abs</span>(k - j) == <span class="built_in">abs</span>(x[j] - x[k])) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">backtrack</span><span class="params">(<span class="type">int</span> t)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (t &gt; n) <span class="built_in">output</span>(x);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = t; i &lt;= n; i++) &#123;</span><br><span class="line">            <span class="built_in">swap</span>(x[t], x[i]);</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">place</span>(t)) <span class="built_in">backtrack</span>(t + <span class="number">1</span>);</span><br><span class="line">            <span class="built_in">swap</span>(x[t], x[i]);</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;本文主要介绍了算法的一些特性和常见算法的实现方法。其中，时间复杂度是算法效率的重要指标，递归是一种常见的算法实现方式，分治法、动态规划、贪心算法和回溯法是常见的算法设计思想。此外，还介绍了哈夫曼编码、单源最短路径、最小生成树、并查集和优先队列等常见算法的实现方法和应用场景。这些算法在计算机科学和工程领域中都有广泛的应用，对于提高算法效率和解决实际问题具有重要意义。&lt;/p&gt;</summary>
    
    
    
    <category term="cs" scheme="https://www.wylu.me/categories/cs/"/>
    
    <category term="data-structure" scheme="https://www.wylu.me/categories/cs/data-structure/"/>
    
    
    <category term="data-structure" scheme="https://www.wylu.me/tags/data-structure/"/>
    
    <category term="algorithm" scheme="https://www.wylu.me/tags/algorithm/"/>
    
  </entry>
  
  <entry>
    <title>CentOS7下Firewall的设置与使用</title>
    <link href="https://www.wylu.me/posts/6a7a0bd6/"/>
    <id>https://www.wylu.me/posts/6a7a0bd6/</id>
    <published>2020-05-16T10:53:39.000Z</published>
    <updated>2022-11-23T17:04:10.543Z</updated>
    
    <content type="html"><![CDATA[<p>防火墙 firewalld 守护服务引入了一个信任级别的概念来管理与之相关联的连接与接口。它支持 ipv4 与 ipv6，并支持网桥，采用 firewall-cmd (command) 或 firewall-config (gui) 来动态的管理 kernel netfilter 的临时或永久的接口规则，并实时生效而无需重启服务。</p><span id="more"></span><div class="note info"><p><strong>[译]CentOS7下Firewall的设置与使用</strong> 原文出处：<a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-using-firewalld-on-centos-7">How To Set Up a Firewall Using FirewallD on CentOS 7</a> 原本作者：Justin Ellingwood 译者：wylu</p></div><h1 id="如何在centos7上使用firewalld设置防火墙">如何在CentOS7上使用FirewallD设置防火墙</h1><h2 id="简介">简介</h2><p>Firewalld 是可用于许多 Linux 发行版的防火墙管理解决方案，它充当 Linux 内核提供的 iptables 数据包过滤系统的前端。在本指南中，我们将介绍如何为服务器设置防火墙，并向您展示使用 <code>firewall-cmd</code> 管理工具管理防火墙的基本知识（如果您希望在 CentOS中 使用 <code>iptables</code>，请遵循<a href="https://www.digitalocean.com/community/tutorials/how-to-migrate-from-firewalld-to-iptables-on-centos-7">本指南</a>）。</p><p>注意：有可能您正在使用比撰写本文时可用的新版本的 firewalld，或者您的服务器设置与本指南中使用的示例服务器略有不同。因此，本指南中说明的某些命令的行为可能会因您的特定配置而异。</p><h2 id="firewalld中的基本概念">Firewalld中的基本概念</h2><p>在开始讨论如何实际使用 <code>firewall-cmd</code> 实用程序来管理防火墙配置之前，我们应该熟悉该工具引入的一些基本概念。</p><h3 id="zones">Zones</h3><p><code>firewalld</code> 守护程序使用称为 "zones" 的实体管理规则组。区域基本上是一组规则，它们决定了允许哪些流量，具体取决于您对计算机所连接的网络的信任程度。为网络接口分配了一个区域，以指示防火墙应允许的行为。</p><p>对于可能经常在网络之间移动的计算机（例如笔记本电脑），这种灵活性提供了一种根据环境更改规则的好方法。在公共WiFi网络上运行时，您可能有严格的规则禁止大多数流量，而在连接到家庭网络时允许放宽限制。对于服务器来说，这些区域并不是那么重要，因为网络环境很少更改（如果有的话）。</p><p>无论您的网络环境有多动态，熟悉防火墙的每个预定义区域背后的一般概念仍然很有用。按从 <strong>最不信任</strong> 到 <strong>最受信任</strong> 的顺序，<code>firewalld</code> 中的预定义区域为：</p><ul><li>drop：最低级别的信任。所有传入的连接都将被丢弃而不会回复，并且只能进行传出连接。</li><li>block：与上述类似，但不仅仅是丢弃连接，而是使用 <code>icmp-host-prohibited</code> 或 <code>icmp6-adm-prohibited</code> 消息拒绝传入的请求。</li><li>public：代表公共的，不受信任的网络。您不信任其他计算机，但可能会视情况允许选择的传入连接。</li><li>external：如果您使用防火墙作为网关，则为外部网络。将其配置为伪装NAT，以便您的内部网络保持私有但可访问。</li><li>internal：外部区域的另一侧，用于网关的内部。这些计算机相当值得信赖，并且可以使用一些其他服务。</li><li>dmz：用于DMZ中的计算机（将无法访问网络其余部分的隔离计算机）。仅允许某些传入连接。</li><li>work：用于工作机。信任网络中的大多数计算机。可能还允许其他一些服务。</li><li>home：家庭环境。通常，这意味着您信任其他大多数计算机，并且将接受其他一些服务。</li><li>trusted：信任网络中的所有计算机。可用选项中最开放的，应谨慎使用。</li></ul><p>要使用防火墙，我们可以创建规则并更改区域的属性，然后将网络接口分配给最合适的区域。</p><h3 id="规则永久性">规则永久性</h3><p>在 firewalld 中，可以将规则指定为 <strong>永久规则</strong> 或 <strong>立即规则</strong>。如果添加或修改规则，则默认情况下，将修改当前运行的防火墙的行为。在下次启动时，旧规则将恢复。</p><p>大多数 <code>firewall-cmd</code> 操作都可以使用 <code>--permanent</code> 标志来指示应将非临时防火墙作为目标。这将影响在引导时重新加载的规则集。这种分离意味着您可以在活动的防火墙实例中测试规则，然后在出现问题时重新加载。您还可以使用 <code>--permanent</code> 标志随着时间的推移建立一套完整的规则，这些规则将在发出 reload 命令时立即应用。</p><h2 id="安装并启用防火墙以在启动时启动">安装并启用防火墙以在启动时启动</h2><p>默认情况下，firewalld 是在某些 Linux 发行版上安装的，包括许多 CentOS 7 映像。但是，您可能需要自己安装 firewalld：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo yum install firewalld</span></span><br></pre></td></tr></table></figure><p>安装 firewalld 之后，您可以启用该服务并重新启动服务器。请记住，启用 firewalld 将导致该服务在服务器启动时启动。最佳实践是在配置此行为之前创建防火墙规则并借此机会对其进行测试，以避免潜在的问题。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo systemctl <span class="built_in">enable</span> firewalld</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo reboot</span></span><br></pre></td></tr></table></figure><p>服务器重新启动时，防火墙会随之启动，然后将网络接口放入配置的区域（或退回到配置的默认区域），并且与该区域关联的所有规则都将应用于关联的接口。</p><p>我们可以通过键入以下内容来验证该服务正在运行并且可以访问：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --state</span></span><br><span class="line">running</span><br></pre></td></tr></table></figure><p>这表明我们的防火墙已启动并以默认配置运行。</p><h2 id="熟悉当前的防火墙规则">熟悉当前的防火墙规则</h2><p>在开始进行修改之前，我们应该熟悉守护程序提供的默认环境和规则。</p><h3 id="探索默认值">探索默认值</h3><p>通过键入以下内容，可以看到当前选择哪个区域作为默认区域：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-default-zone</span></span><br><span class="line">public</span><br></pre></td></tr></table></figure><p>由于我们没有给 firewalld 提供任何偏离默认区域的命令，并且我们的接口都没有配置为绑定到另一个区域，因此该区域（public 区域）也将是唯一的“活动”（active）区域（该区域正在控制我们接口的流量）。我们可以通过输入以下内容进行验证：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-active-zones</span></span><br><span class="line">public</span><br><span class="line">  interfaces: eth0 eth1</span><br></pre></td></tr></table></figure><p>在这里，我们可以看到示例服务器具有两个受防火墙控制的网络接口（ <code>eth0</code> 和 <code>eth1</code> ）。目前，它们都根据为 public 区域定义的规则进行管理。</p><p>我们如何知道与 public 区域相关的规则？ 我们可以通过输入以下内容来打印默认区域的配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --list-all</span></span><br><span class="line">public (default, active)</span><br><span class="line">  target: default</span><br><span class="line">  icmp-block-inversion: no</span><br><span class="line">  interfaces: eth0 eth1</span><br><span class="line">  sources:</span><br><span class="line">  services: ssh dhcpv6-client</span><br><span class="line">  ports:</span><br><span class="line">  protocols:</span><br><span class="line">  masquerade: no</span><br><span class="line">  forward-ports:</span><br><span class="line">  source-ports:</span><br><span class="line">  icmp-blocks:</span><br><span class="line">  rich rules:</span><br></pre></td></tr></table></figure><p>从输出中可以看出，该区域既是默认区域（default zone）又是活动区域（active zone），并且 <code>eth0</code> 和 <code>eth1</code> 接口与此区域相关联（我们已经从之前的查询中了解了所有这些信息）。但是，我们还可以看到，该区域允许与 DHCP 客户端（用于 IP 地址分配）和 SSH（用于远程管理）相关的正常操作。</p><h3 id="探索可选区域">探索可选区域</h3><p>现在，我们对默认区域和活动区域的配置有了一个好主意。我们还可以找到有关其他区域的信息。</p><p>要获取可用区域的列表，请输入：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-zones</span></span><br><span class="line">block dmz drop external home internal public trusted work</span><br></pre></td></tr></table></figure><p>通过在 <code>--list-all</code> 命令中指定 <code>--zone</code> 参数，可以看到与指定区域关联的特定配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=home --list-all</span></span><br><span class="line">home</span><br><span class="line">  interfaces:</span><br><span class="line">  sources:</span><br><span class="line">  services: dhcpv6-client ipp-client mdns samba-client ssh</span><br><span class="line">  ports:</span><br><span class="line">  masquerade: no</span><br><span class="line">  forward-ports:</span><br><span class="line">  icmp-blocks:</span><br><span class="line">  rich rules:</span><br></pre></td></tr></table></figure><p>您可以使用 <code>--list-all-zones</code> 选项输出所有区域定义。您可能希望将输出通过管道传到 pager 中以便于查看：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --list-all-zones | less</span></span><br></pre></td></tr></table></figure><h2 id="为接口选定区域">为接口选定区域</h2><p>除非另外配置了网络接口，否则在启动防火墙时，每个接口都将置于默认区域中。</p><h3 id="更改接口区域">更改接口区域</h3><p>通过将 <code>--zone</code> 参数与 <code>--change-interface</code> 参数结合使用，可以在会话期间在区域之间转换接口。与所有修改防火墙的命令一样，您将需要使用 sudo。</p><p>例如，我们可以通过输入以下命令将 eth0 接口转换为 "home" 区域：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=home --change-interface=eth0</span></span><br><span class="line">success</span><br></pre></td></tr></table></figure><p>注意：每当将接口转换到新区域时，请注意您可能正在修改将要运行的服务。例如，在这里，我们将移至具有 SSH 可用的 "home" 区域。这意味着我们的连接不应断开。其他一些区域默认情况下未启用 SSH，如果在使用这些区域之一时断开连接，您可能会发现自己无法重新登录。</p><p>我们可以通过再次请求活动区域来验证此操作是否成功：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-active-zones</span></span><br><span class="line">home</span><br><span class="line">  interfaces: eth0</span><br><span class="line">public</span><br><span class="line">  interfaces: eth1</span><br></pre></td></tr></table></figure><h3 id="调整默认区域">调整默认区域</h3><p>您最好用一个区域来处理所有接口，这样选择最佳的默认区域然后将其用于配置可能会更容易。</p><p>您可以使用 <code>--set-default-zone</code> 参数更改默认区域。这将立即将所有使用默认值的接口更改为新区域：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --set-default-zone=home</span></span><br><span class="line">success</span><br></pre></td></tr></table></figure><h2 id="为您的应用程序设置规则">为您的应用程序设置规则</h2><p>为希望提供的服务定义防火墙例外的基本方法很容易。我们将在此处介绍基本概念。</p><h3 id="将服务添加到您的区域">将服务添加到您的区域</h3><p>最简单的方法是将所需的服务或端口添加到正在使用的区域。同样，您可以使用 <code>--get-services</code> 选项获取可用服务的列表：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-services</span></span><br><span class="line">RH-Satellite-6 amanda-client amanda-k5-client bacula bacula-client bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc ceph ceph-mon cfengine condor-collector ctdb dhcp dhcpv6 dhcpv6-client dns docker-registry dropbox-lansync elasticsearch freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master high-availability http https imap imaps ipp ipp-client ipsec iscsi-target kadmin kerberos kibana klogin kpasswd kshell ldap ldaps libvirt libvirt-tls managesieve mdns mosh mountd ms-wbt mssql mysql nfs nrpe ntp openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy proxy-dhcp ptp pulseaudio puppetmaster quassel radius rpc-bind rsh rsyncd samba samba-client sane sip sips smtp smtp-submission smtps snmp snmptrap spideroak-lansync squid ssh synergy syslog syslog-tls telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server</span><br></pre></td></tr></table></figure><p>注意：您可以通过查看 <code>/usr/lib/firewalld/services</code> 目录中与它们相关的 <code>.xml</code> 文件来获得有关每个服务的更多详细信息。例如，SSH 服务（/usr/lib/firewalld/services/ssh.xml）的定义如下：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">service</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">short</span>&gt;</span>SSH<span class="tag">&lt;/<span class="name">short</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">description</span>&gt;</span>Secure Shell (SSH) is a protocol for logging into and executing commands on remote machines. It provides secure encrypted communications. If you plan on accessing your machine remotely via SSH over a firewalled interface, enable this option. You need the openssh-server package installed for this option to be useful.<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">port</span> <span class="attr">protocol</span>=<span class="string">&quot;tcp&quot;</span> <span class="attr">port</span>=<span class="string">&quot;22&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">service</span>&gt;</span></span><br></pre></td></tr></table></figure><p>您可以使用 <code>--add-service</code> 参数为区域启用服务。该操作将针对默认区域或 <code>--zone</code> 参数指定的任何区域。默认情况下，这只会调整当前的防火墙会话。您可以通过包括 <code>--permanent</code> 标志来调整永久防火墙配置。</p><p>例如，如果我们运行的是提供常规 HTTP 流量的 Web 服务器，则可以通过键入以下内容在此会话的 "public" 区域中为接口允许此流量：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --add-service=http</span></span><br></pre></td></tr></table></figure><p>如果要修改默认区域，可以省略 <code>--zone</code>。我们可以使用 <code>--list-all</code> 或 <code>--list-services</code> 操作来验证操作是否成功：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --list-services</span></span><br><span class="line">dhcpv6-client http ssh</span><br></pre></td></tr></table></figure><p>测试完所有功能后，您可能需要修改永久防火墙规则，以便重新启动后您的服务仍然可用。我们可以通过输入以下内容使 "public" 区域更改永久生效：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --permanent --add-service=http</span></span><br><span class="line">success</span><br></pre></td></tr></table></figure><p>您可以通过在 <code>--list-services</code> 操作中添加 <code>--permanent</code> 标志来验证此操作是否成功。您需要对任何 <code>--permanent</code> 操作使用sudo：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --permanent --list-services</span></span><br><span class="line">dhcpv6-client http ssh</span><br></pre></td></tr></table></figure><p>您的 "public" 区域现在将允许端口 80 上的 HTTP Web 通信。如果您的 Web 服务器配置为使用 SSL/TLS，则还需要添加 https 服务。我们可以通过输入以下内容将其添加到当前会话和永久规则集中：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --add-service=https</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --permanent --add-service=https</span></span><br></pre></td></tr></table></figure><h3 id="如果没有适当的服务可用怎么办">如果没有适当的服务可用怎么办</h3><p>防火墙安装中包含的防火墙服务代表了您可能希望允许访问的应用程序的许多最常见要求。但是，在某些情况下，这些服务可能无法满足您的要求。</p><p>在这种情况下，您有两个选择。</p><h4 id="为您的区域打开端口">为您的区域打开端口</h4><p>为您的特定应用程序添加支持的最简单方法是打开它在相应区域中使用的端口。这就像指定端口或端口范围以及需要打开的端口的关联协议一样容易。</p><p>例如，如果我们的应用程序在端口 5000 上运行并使用 TCP，则可以使用 <code>--add-port</code> 参数将其添加到此会话的 "public" 区域中。协议可以是 tcp 或 udp：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --add-port=5000/tcp</span></span><br><span class="line">success</span><br></pre></td></tr></table></figure><p>我们可以使用 <code>--list-ports</code> 操作验证此操作是否成功：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --list-ports</span></span><br><span class="line">5000/tcp</span><br></pre></td></tr></table></figure><p>也可以通过用 <code>-</code> 分隔范围内的开始和结束端口来指定顺序的端口范围。例如，如果我们的应用程序使用 UDP 端口 4990 至 4999，则可以通过键入以下内容在 "public" 端口上打开这些端口：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --add-port=4990-4999/udp</span></span><br></pre></td></tr></table></figure><p>经过测试后，我们可能希望将它们添加到永久防火墙中。您可以通过键入以下内容进行操作：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --permanent --add-port=5000/tcp</span></span><br><span class="line">success</span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --permanent --add-port=4990-4999/udp</span></span><br><span class="line">success</span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=public --permanent --list-ports</span></span><br><span class="line">5000/tcp 4990-4999/udp</span><br></pre></td></tr></table></figure><h4 id="定义服务">定义服务</h4><p>为您的区域打开端口很容易，但是很难跟踪每个区域的用途。如果您曾经停用服务器上的服务，则可能很难记住仍需要打开哪些端口。为了避免这种情况，可以定义服务。</p><p>服务只是带有相关名称和描述的端口的简单集合。使用服务比端口更易于管理，但是需要一些前期工作。最简单的开始方法是将现有脚本（在 /usr/lib/firewalld/services 中找到）复制到 /etc/firewalld/services 目录中，防火墙在该目录中查找非标准定义。</p><p>例如，我们可以复制 SSH 服务定义以用于我们的 "example" 服务定义，如下所示。文件名减去 <code>.xml</code> 后缀将指示防火墙服务列表中的服务名称：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo <span class="built_in">cp</span> /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/example.xml</span></span><br></pre></td></tr></table></figure><p>现在，您可以调整在复制的文件中找到的定义：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo vi /etc/firewalld/services/example.xml</span></span><br></pre></td></tr></table></figure><p>首先，文件将包含您复制的 SSH 定义：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">service</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">short</span>&gt;</span>SSH<span class="tag">&lt;/<span class="name">short</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">description</span>&gt;</span>Secure Shell (SSH) is a protocol for logging into and executing commands on remote machines. It provides secure encrypted communications. If you plan on accessing your machine remotely via SSH over a firewalled interface, enable this option. You need the openssh-server package installed for this option to be useful.<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">port</span> <span class="attr">protocol</span>=<span class="string">&quot;tcp&quot;</span> <span class="attr">port</span>=<span class="string">&quot;22&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">service</span>&gt;</span></span><br></pre></td></tr></table></figure><p>该定义的大部分实际上是元数据。您将要在 <code>&lt;short&gt;</code> 标记内更改服务的简称。这是您的服务的易读名称。您还应该添加描述，以便在需要审核服务时获得更多信息。您需要进行的实际上会影响服务功能的唯一配置可能是端口定义，您可以在其中定义要打开的端口号和协议。可以多次指定。</p><p>对于我们的 "example" 服务，假设我们需要为 TCP 打开端口 7777，为 UDP 打开端口 8888。通过按 <code>i</code> 进入 INSERT 模式，我们可以使用以下内容修改现有定义：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">service</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">short</span>&gt;</span>Example Service<span class="tag">&lt;/<span class="name">short</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">description</span>&gt;</span>This is just an example service.  It probably shouldn&#x27;t be used on a real system.<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">port</span> <span class="attr">protocol</span>=<span class="string">&quot;tcp&quot;</span> <span class="attr">port</span>=<span class="string">&quot;7777&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">port</span> <span class="attr">protocol</span>=<span class="string">&quot;udp&quot;</span> <span class="attr">port</span>=<span class="string">&quot;8888&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">service</span>&gt;</span></span><br></pre></td></tr></table></figure><p>按 ESC，然后输入 <code>:x</code> 保存并关闭文件。</p><p>重新加载防火墙以访问新服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --reload</span></span><br></pre></td></tr></table></figure><p>您可以看到它现在在可用服务列表中：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-services</span></span><br><span class="line">RH-Satellite-6 amanda-client amanda-k5-client bacula bacula-client bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc ceph ceph-mon cfengine condor-collector ctdb dhcp dhcpv6 dhcpv6-client dns docker-registry dropbox-lansync elasticsearch example freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master high-availability http https imap imaps ipp ipp-client ipsec iscsi-target kadmin kerberos kibana klogin kpasswd kshell ldap ldaps libvirt libvirt-tls managesieve mdns mosh mountd ms-wbt mssql mysql nfs nrpe ntp openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy proxy-dhcp ptp pulseaudio puppetmaster quassel radius rpc-bind rsh rsyncd samba samba-client sane sip sips smtp smtp-submission smtps snmp snmptrap spideroak-lansync squid ssh synergy syslog syslog-tls telnet tftp tftp-client tinc tor-socks transmission-client vdsm vnc-server wbem-https xmpp-bosh xmpp-client xmpp-local xmpp-server</span><br></pre></td></tr></table></figure><p>现在，您可以像往常一样在您的区域中使用此服务。</p><h2 id="创建自己的区域">创建自己的区域</h2><p>尽管预定义的区域对于大多数用户来说可能绰绰有余，但是定义自己的区域来描述其功能可能会有所帮助。</p><p>例如，您可能要为 Web 服务器创建一个名为 "publicweb" 的区域。或者，您可能希望为您在专用网络上提供的 DNS 服务配置另一个区域，您可能需要一个名为 "privateDNS" 的区域。</p><p>添加区域时，必须将其添加到永久防火墙配置中。然后，您可以重新加载以将配置带入正在运行的会话。例如，我们可以通过键入以下内容来创建我们上面讨论的两个区域：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --permanent --new-zone=publicweb</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --permanent --new-zone=privateDNS</span></span><br></pre></td></tr></table></figure><p>您可以通过键入以下内容来验证这些内容是否存在于您的永久配置中：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --permanent --get-zones</span></span><br><span class="line">block dmz drop external home internal privateDNS public publicweb trusted work</span><br></pre></td></tr></table></figure><p>如前所述，这些功能在当前的防火墙实例中尚不可用：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-zones</span></span><br><span class="line">block dmz drop external home internal public trusted work</span><br></pre></td></tr></table></figure><p>重新加载防火墙以使这些新区域进入活动配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --reload</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-zones</span></span><br><span class="line">block dmz drop external home internal privateDNS public publicweb trusted work</span><br></pre></td></tr></table></figure><p>现在，您可以开始为您的区域分配适当的服务和端口。通常，最好先调整活动实例，然后在测试后将这些更改转移到永久配置中。例如，对于 "publicweb" 区域，您可能想要添加 SSH，HTTP 和 HTTPS 服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --add-service=ssh</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --add-service=http</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --add-service=https</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --list-all</span></span><br><span class="line">publicweb</span><br><span class="line">  target: default</span><br><span class="line">  icmp-block-inversion: no</span><br><span class="line">  interfaces:</span><br><span class="line">  sources:</span><br><span class="line">  services: ssh http https</span><br><span class="line">  ports:</span><br><span class="line">  protocols:</span><br><span class="line">  masquerade: no</span><br><span class="line">  forward-ports:</span><br><span class="line">  source-ports:</span><br><span class="line">  icmp-blocks:</span><br><span class="line">  rich rules:</span><br></pre></td></tr></table></figure><p>同样地，我们可以将 DNS 服务添加到我们的 "privateDNS" 区域：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=privateDNS --add-service=dns</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=privateDNS --list-all</span></span><br><span class="line">privateDNS</span><br><span class="line">  interfaces:</span><br><span class="line">  sources:</span><br><span class="line">  services: dns</span><br><span class="line">  ports:</span><br><span class="line">  masquerade: no</span><br><span class="line">  forward-ports:</span><br><span class="line">  icmp-blocks:</span><br><span class="line">  rich rules:</span><br></pre></td></tr></table></figure><p>然后，我们可以将网络接口更改为这些新区域以对其进行测试：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --change-interface=eth0</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=privateDNS --change-interface=eth1</span></span><br></pre></td></tr></table></figure><p>此时，您将有机会测试您的配置。如果这些值对您有用，您将要向永久配置添加相同的规则。您可以通过使用 <code>--permanent</code> 标志重新应用规则来做到这一点：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --permanent --add-service=ssh</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --permanent --add-service=http</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --permanent --add-service=https</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=privateDNS --permanent --add-service=dns</span></span><br></pre></td></tr></table></figure><p>在永久应用了这些规则之后，您可以重新启动网络并重新加载防火墙服务：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo systemctl restart network</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo systemctl reload firewalld</span></span><br></pre></td></tr></table></figure><p>验证是否分配了正确的区域：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">firewall-cmd --get-active-zones</span></span><br><span class="line">privateDNS</span><br><span class="line">  interfaces: eth1</span><br><span class="line">publicweb</span><br><span class="line">  interfaces: eth0</span><br></pre></td></tr></table></figure><p>并验证相应的服务可用于两个区域：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=publicweb --list-services</span></span><br><span class="line">http https ssh</span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --zone=privateDNS --list-services</span></span><br><span class="line">dns</span><br></pre></td></tr></table></figure><p>您已经成功设置了自己的区域！ 如果要将这些区域之一设为其他接口的默认区域，请记住使用 <code>--set-default-zone</code> 参数配置该行为：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo firewall-cmd --set-default-zone=publicweb</span></span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><p>您现在应该对如何在 CentOS 系统上日常使用的防火墙服务进行充分的了解。</p><p>Firewalld 服务允许您配置考虑网络环境的可维护规则和规则集。它使您可以通过使用区域在不同的防火墙策略之间无缝过渡，并使管理员能够将端口管理抽象为更友好的服务定义。掌握该系统的使用知识将使您能够利用此工具提供的灵活性和强大功能。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;防火墙 firewalld 守护服务引入了一个信任级别的概念来管理与之相关联的连接与接口。它支持 ipv4 与 ipv6，并支持网桥，采用 firewall-cmd (command) 或 firewall-config (gui) 来动态的管理 kernel netfilter 的临时或永久的接口规则，并实时生效而无需重启服务。&lt;/p&gt;</summary>
    
    
    
    <category term="os" scheme="https://www.wylu.me/categories/os/"/>
    
    <category term="centos" scheme="https://www.wylu.me/categories/os/centos/"/>
    
    
    <category term="centos" scheme="https://www.wylu.me/tags/centos/"/>
    
    <category term="firewall" scheme="https://www.wylu.me/tags/firewall/"/>
    
  </entry>
  
  <entry>
    <title>进程间通信机制</title>
    <link href="https://www.wylu.me/posts/9fb7f5b4/"/>
    <id>https://www.wylu.me/posts/9fb7f5b4/</id>
    <published>2020-05-10T14:51:26.000Z</published>
    <updated>2022-11-23T17:04:10.543Z</updated>
    
    <content type="html"><![CDATA[<p>进程通信（Inter-Process Communication, IPC）是指进程之间的信息交换。其所交换的信息量，少则是一个状态或数值，多则是成千上万个字节。多个进程为了协调完成一项工作，相互之间必须能够进行通信。进程的互斥和同步可归结为低级通信。进程的高级通信是指用户可直接利用系统所提供的一组通信命令，高效地传送大量数据的一种通信方式。操作系统隐藏了进程通信的实现细节，即对用户来说是透明的。这样就大大简化了通信程序编程上的复杂性。</p><span id="more"></span><h1 id="进程间通信机制">进程间通信机制</h1><p>Linux 操作系统支持以下几种进程间通信机制：</p><ul><li>Unix IPC 机制<ul><li>信号（signal）</li><li>管道（pipe）和命名管道（named pipe）</li></ul></li><li>System V 的 IPC 机制<ul><li>信号量（semaphore）</li><li>消息队列（message queue）</li><li>共享内存（shared memory）</li></ul></li><li>网络通信的套接字机制<ul><li>套接字（socket）</li></ul></li><li>全双工管道机制</li></ul><p>以上各种通信机制都提供了相应的接口，IPC 结构图如下所示：</p><p><img src="https://cdn.jsdelivr.net/gh/wylu/img/e7555aebbcdae382572ddedda53516e8.png" alt="IPC" /></p><h2 id="信号">信号</h2><p>信号是用来向一个或多个进程发送异步事件的软件机制，它类似于硬件中断，所以也叫软中断。信号不仅可以从键盘中断中产生，进程对虚拟内存的非法存取等系统错误环境下也会有信号产生，此外信号还被 shell 程序用来向其子进程发送任务控制命令。</p><p>系统中有一组被详细定义的信号类型，使用 <code>man 7 signal</code> 可以看到详细介绍。除了 SIGSTOP（进程终止执行）和 SIGKILL（进程退出）两个信号外，进程可以忽略其余信号。信号没有相对优先级，如果在同一时刻对于一个进程产生了两个信号，则它们将可能以任意顺序到达进程并进行处理。同时 Linux 并不提供处理多个相同类型信号的方式。</p><p>信号个数受到处理器字长的限制。32 位字长的处理器最多可以有 32 个信号，而 64 位处理器可以有最多 64 个信号。Linux 通过存储在进程 task_struct 中的信息来实现信号。当前未处理的信号保存在 signal 域中，并带有保存在 blocked 中的被阻塞信号的屏蔽码。除了 SIGSTOP 和 SIGKILL 外，所有的信号都能被阻塞。</p><p>系统中只有核心和超级用户进程可以向其他所有进程发送信号，普通进程只能向具有相同 uid 和 gid 的进程或者在同一进程组中的进程发送信号。信号是通过设置 task_struct 结构中 signal 域里的某一位来产生的。如果进程没有阻塞信号并且处于可中断的等待状态，则可以将其状态改成 Running，同时如确认进程还处在运行队列中，就可以通过信号唤醒它。这样系统下次发生调度时，调度管理器将选择它运行。信号必须等待到进程再次运行时才交给它，每次进程从系统调用中退出前，它都会检查 signal 和 blocked 域，看是否有可以立刻发送的非阻塞信号。对当前不可阻塞信号的处理代码放置在 sigaction 结构中。</p><h2 id="管道">管道</h2><p>管道是一个先进先出、大小固定的缓冲区，容量为 1 页（4KB），用于两个进程之间的单向数据传递。当管道有空间时，写者进程把数据送入管道，否则将被阻塞；如果管道中没有数据或读者进程需要的数据多于其中的数据，读者进程会被阻塞，否则执行读者进程的请求。整个过程由操作系统监控完成，互斥地访问管道。当传送的数据量大于管道的容量时，可以通过同步机制分次传送数据。</p><h3 id="无名管道">无名管道</h3><p>例如：Linux shell 程序中的重定向操作：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">ls</span> -l | more</span></span><br></pre></td></tr></table></figure><p>在这个管道应用中，<code>ls</code> 列出当前目录的输出被作为标准输入送到 more 程序中按格式显示处理。无名管道是将两个相关联的进程联系在一起。shell 程序负责在进程间建立临时的管道。</p><p>在 Linux 中，管道是通过指向同一个临时 VFS（Virtual File System，虚拟文件系统）inode 的两个 file 数据结构来实现的，此 VFS inode 指向内存中的一个物理块。当写者进程向管道中写入数据时，字节被复制到共享数据页面中，当读者进程从管道中读时，字节从共享数据页面中复制出来。</p><p>Linux 必须同步对管道的访问。它必须保证读者和写者进程以确定的步骤执行，为此需要使用锁、等待队列和信号等同步机制。以写数据为例，如果没有足够的空间容纳对所有写入管道的数据，或者管道被读者进程加锁时，当前进程将在管道 inode 的等待队列中睡眠，同时调度管理器开始执行以选择其他进程来执行。当有足够的空间或者管道被解锁时，它将被读者唤醒，然后执行写操作。当两个进程对管道的使用结束时，管道 inode 和共享数据页面将同时被释放。</p><h3 id="命名管道">命名管道</h3><p>Linux 还支持命名管道（named pipe），即 FIFO 管道，为两个不相关的进程提供通信手动。命名管道不是临时对象，它们是文件系统中的实体并且可以通过 <code>mknod</code> 命令来创建。进程只要拥有适当的权限就可以自由使用 FIFO 管道。在写者进程使用之前，Linux 必须让读者进程先打开此 FIFO 管道；任何读者进程从中读取之前必须有写者进程向其写入数据。</p><h2 id="消息队列">消息队列</h2><p>Linux 系统也支持 System V 的进程间通信机制，包括消息序列、信号量和共享内存。所有的 IPC 对象在 Linux 中有一个公共的 ipc_perm 结构，它含有进程拥有者、创建者和组标志符，对此对象（拥有者，组及其他）的存取模式以及 IPC 对象键。</p><p>消息是按一定格式封装起来的消息。每个进程都有一个与之关联的消息队列，接收进程按时间顺序或消息类型从消息队列取走消息。如果进程向一个满队列发送消息或从一个空队列取走消息都会被阻塞。</p><p>Linux 系统维护着一个 msgque 消息队列链表，其中每个元素指向一个描述消息队列 msqid_ds 结构，该结构完整地描述一个消息队列。每个 msqid_ds 结构包含一个 ipc_perm 结构和指向已经进入此队列消息的指针。另外，Linux 保留有关队列修改时间信息，如上次系统向队列中写入的时间等。msqid_ds 包含两个等待队列：一个为队列写入进程使用而另一个由队列读取进程使用。</p><p>每次进程试图向写入队列写入消息时，系统将把其有效用户和组标志符与此队列的 ipc_perm 结构中的模式进行比较。如果允许写入操作，则把此消息从该进程的地址空间复制到 msg 数据结构中，并放置到此消息队列尾部。如果消息队列的长度已满，则该写入进程将被阻塞，并调度新进程运行。若有消息被取走时，该进程将被唤醒。读进程执行时将选择队列中第一个消息或者某特定类型的消息。如果没有消息可以满足此要求，读进程将被阻塞，并运行调度程序。当有新消息写入队列时，进程将被唤醒。</p><h2 id="信号量">信号量</h2><p>信号量是用一个整数表示系统当前资源的使用情况，当信号量大于或等于 0 时，其值表示可用资源的数量，当它小于 0 时，其值表示等待该资源的进程数。信号量是 wait 和 signal 原语的推广，可以通过它实现进程的同步与互斥。例如用信号量实现临界区（critical region）的互斥，即在某一时刻在此区域内的代码只能被一个进程执行。</p><h3 id="数据结构">数据结构</h3><p>信号量在 Linux 中使用以下几个数据结构来表示：</p><ul><li>sem：表示系统中的每个信号量</li><li>semid_ds：表示信号量的集合</li><li>sem_queue：表示由每个信号量集合所构成的队列</li></ul><p>semid_ds 结构的 sem_base 指向一个 sem 数组，进程可以使用系统调用来操作这些信号量数组。</p><h3 id="实现过程">实现过程</h3><p>在执行信号量操作时，Linux 首先将检查是否所有操作已经成功。如果操作值与信号量当前数组相加大于 0，或者操作值与信号量当前值都是 0，操作将会成功。如果所有信号量操作失败，Linux 仅仅会把那些操作标志没有要求系统调用为非阻塞类型的进程挂起。进程挂起后，Linux 必须保存信号量操作的执行状态并将当前进程放入等待队列。系统还堆栈上建立 sem_queue 结构并填充各个域。 这个 sem_queue 结构将被放到信号量对象等待队列的尾部。系统把当前进程置入 sem_queue 结构中的等待队列中，然后执行调度程序。</p><p>如果所有这些信号量操作都成功则无须挂起当前进程，Linux 将对信号量数组中的其他成员进行相同操作，然后检查那些处于等待或挂起状态的进程。首先，Linux 将依次检查挂起队列（sem_pending）中的每个成员，看信号量操作能否继续。如果可以则将其 sem_queue 结构从挂起链表中删除并对信号量数组发出信号灯操作。Linux 还将唤醒处于睡眠状态的进程并使之成为下一个运行的进程。如果在对挂起队列的遍历过程中有的信号量操作不能完成则 Linux 将一直重复此过程，直到所有信号量操作完成且没有进程需要继续睡眠。</p><h3 id="与信号量有关的系统调用">与信号量有关的系统调用</h3><ul><li><code>semget()</code>：创建新的信号量集合或存取一个已有的信号量集合。</li><li><code>semop()</code>： 当操作数和信号量的值相加大于或等于 0 时，即进程请求的资源能够满足，操作成功，返回 0；若相加后其中某个值小于 0，资源不能满足，将进程阻塞，操作不成功。</li><li><code>semctl()</code>：在信号量集合上完成指定的命令操作。</li></ul><h3 id="死锁">死锁</h3><p>死锁是信号量使用过程中可能产生的一个最严重的问题。当一个进程进入临界区时它修改了信号量的值，然后在离开临界区时由于运行失败或者被 kill 而没有改回信号量时，死锁将会发生。</p><p>Linux 为了避免死锁的发生，为每个进程维护至少一个对应于信号量数组的 sem_undo 结构，它保存了完成信号量操作之前的状态。当对信号量进行操作时，信号量变化的数值被放入进程的 sem_undo 结构的该信号的入口中。当进程被删除时，Linux 将遍历该进程的 sem_undo 集合对信号量数组使用调整值。如果信号量集合被删除而 sem_undo 数据结构还在进程的 task_struct 结构中，则此信号灯数组标志符将被置为无效。此时信号量清除代码只需丢弃 sem_undo 即可。</p><h2 id="共享存储区">共享存储区</h2><p>共享存储区是指被多个进程共享的虚存中的一个数据块，进程利用它来实现通信。此虚拟内存的页面出现在每个共享进程页表中，但位置可以不同。每个进程有相应的读或读写权限，对共享存储区的互斥访问必须依赖于其他机制，如信号量。</p><p>每个新创建的共享存储区由一个 shmid_ds 数据结构来表示，它描述共享存储区的大小，进程如何使用以及共享存储区映射到各自地址空间的方式。由共享存储区创建者控制对此内存的存取权限。</p><p>每个使用此共享内存的进程必须它能通过系统调用将其连接到虚拟内存上，但在连接时并没有创建共享存储区，只有进程访问它时才创建。当进程首次访问共享虚拟内存中的页面时将产生缺页中断。当取回此页面后，Linux 找到了描述此页面的数据结构。它包含指向使用此种类型虚拟内存的处理函数地址指针。当发生共享内存页面缺页错误时，错误处理代码将在此 shmid_ds 对应的页表入口链表中寻找此页面是否在内存。如果不在，则为其分配物理页面并创建页表入口。同时还将它放入当前进程的页表中，此入口被保存在 shmid_ds 结构中。下个访问此内存的进程就会连接到此页面上。这样，第一个访问虚拟内存页面的进程创建这块内存，随后的进程把此页面加入到各自的虚拟地址空间中。</p><p>不再使用共享存储区的进程将断开与之的连接，其对应的页面结构将从 shmid_ds 结构中删除并回收。当前进程对应此共享内存地址的页表入口也将被更新并置为无效。当最后一个进程断开与共享内存的连接时，当前位于物理内存中的共享内存页面将被释放，同时删除 shmid_ds 结构。</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;进程通信（Inter-Process Communication, IPC）是指进程之间的信息交换。其所交换的信息量，少则是一个状态或数值，多则是成千上万个字节。多个进程为了协调完成一项工作，相互之间必须能够进行通信。进程的互斥和同步可归结为低级通信。进程的高级通信是指用户可直接利用系统所提供的一组通信命令，高效地传送大量数据的一种通信方式。操作系统隐藏了进程通信的实现细节，即对用户来说是透明的。这样就大大简化了通信程序编程上的复杂性。&lt;/p&gt;</summary>
    
    
    
    <category term="os" scheme="https://www.wylu.me/categories/os/"/>
    
    <category term="kernel" scheme="https://www.wylu.me/categories/os/kernel/"/>
    
    
    <category term="进程间通信" scheme="https://www.wylu.me/tags/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1/"/>
    
    <category term="IPC" scheme="https://www.wylu.me/tags/IPC/"/>
    
  </entry>
  
</feed>
