<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>应用开发笔记</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://www.pocketdigi.com/"/>
  <updated>2019-09-06T09:47:13.551Z</updated>
  <id>http://www.pocketdigi.com/</id>
  
  <author>
    <name>pocketdigi</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>国内环境kubernetes安装配置</title>
    <link href="http://www.pocketdigi.com/2019-09/install-k8s-in-china.html"/>
    <id>http://www.pocketdigi.com/2019-09/install-k8s-in-china.html</id>
    <published>2019-09-06T09:34:52.000Z</published>
    <updated>2019-09-06T09:47:13.551Z</updated>
    
    <content type="html"><![CDATA[<h2 id="环境"><a href="#环境" class="headerlink" title="环境"></a>环境</h2><p> 操作系统: CentOS Linux release 7.6.1810</p><p> 内网ip: 192.168.20.15</p><p> kubernetes: v1.15.1</p><p> docker: 19.03.1</p><h2 id="安装docker"><a href="#安装docker" class="headerlink" title="安装docker"></a>安装docker</h2> <figure class="highlight plain"><table><tr><td class="gutter"><pre><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">curl -fsSL get.docker.com -o get-docker.sh</span><br><span class="line">sh get-docker.sh --mirror Aliyun</span><br><span class="line">systemctl enable docker</span><br><span class="line">systemctl start docker</span><br></pre></td></tr></table></figure> <a id="more"></a><h2 id="添加docker国内镜像"><a href="#添加docker国内镜像" class="headerlink" title="添加docker国内镜像"></a>添加docker国内镜像</h2> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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 &lt;&lt;EOF &gt; /etc/docker/daemon.json</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;log-driver&quot;: &quot;json-file&quot;,</span><br><span class="line">  &quot;log-opts&quot;: &#123;</span><br><span class="line">    &quot;max-size&quot;: &quot;100m&quot;</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;storage-driver&quot;: &quot;overlay2&quot;,</span><br><span class="line">  &quot;storage-opts&quot;: [</span><br><span class="line">    &quot;overlay2.override_kernel_check=true&quot;</span><br><span class="line">  ],</span><br><span class="line">  &quot;debug&quot; : true,</span><br><span class="line">  &quot;insecure-registries&quot; : [</span><br><span class="line">    &quot;nas.pocketdigi.com:8083&quot;</span><br><span class="line">  ],</span><br><span class="line">  &quot;experimental&quot; : false,</span><br><span class="line">  &quot;registry-mirrors&quot; : [</span><br><span class="line">    &quot;https://registry.docker-cn.com&quot;,</span><br><span class="line">    &quot;https://reg-mirror.qiniu.com&quot;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line">systemctl restart docker</span><br><span class="line">docker login nas.pocketdigi.com:8083</span><br></pre></td></tr></table></figure><p>nas.pocketdigi.com:8083 是私有仓库地址，不支持https，所以需要放到insecure-registries,没有私有仓库就不需要</p><h2 id="添加阿里云源-安装kubelet-kubeadm-kubectl"><a href="#添加阿里云源-安装kubelet-kubeadm-kubectl" class="headerlink" title="添加阿里云源,安装kubelet,kubeadm,kubectl"></a>添加阿里云源,安装kubelet,kubeadm,kubectl</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></pre></td><td class="code"><pre><span class="line">cat &lt;&lt;EOF &gt; /etc/yum.repos.d/kubernetes.repo</span><br><span class="line">[kubernetes]</span><br><span class="line">name=Kubernetes</span><br><span class="line">baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/</span><br><span class="line">enabled=1</span><br><span class="line">gpgcheck=1</span><br><span class="line">repo_gpgcheck=1</span><br><span class="line">gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg</span><br><span class="line">EOF</span><br><span class="line">setenforce 0</span><br><span class="line">yum install -y kubelet kubeadm kubectl</span><br><span class="line">systemctl enable kubelet &amp;&amp; systemctl start kubelet</span><br></pre></td></tr></table></figure><h2 id="配置iptables"><a href="#配置iptables" class="headerlink" title="配置iptables"></a>配置iptables</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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">cat &lt;&lt;EOF &gt;  /etc/sysctl.d/k8s.conf</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">EOF</span><br><span class="line">sysctl --system</span><br></pre></td></tr></table></figure><h2 id="关闭swap"><a href="#关闭swap" class="headerlink" title="关闭swap"></a>关闭swap</h2><p>注释掉/etc/fstab文件里swap分区，重启</p><h2 id="创建集群"><a href="#创建集群" class="headerlink" title="创建集群"></a>创建集群</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">kubeadm init --apiserver-advertise-address=192.168.20.15 --pod-network-cidr=10.244.0.0/16 --apiserver-cert-extra-sans=nas.pocketdigi.com</span><br></pre></td></tr></table></figure><p>因为服务器没有外网ip,是通过nginx转发的,必须通过<code>-apiserver-cert-extra-sans</code> 配置最终访问的外网ip或域名</p><p>如果是国内服务器，因为k8s仓库被墙，会报以下错误:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><br><span class="line">[preflight] You can also perform this action in beforehand using &apos;kubeadm config images pull&apos;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">error execution phase preflight: [preflight] Some fatal errors occurred:</span><br><span class="line">[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-apiserver:v1.15.1: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)</span><br><span class="line">, error: exit status 1</span><br><span class="line">[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-controller-manager:v1.15.1: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)</span><br><span class="line">, error: exit status 1</span><br><span class="line">[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-scheduler:v1.15.1: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)</span><br><span class="line">, error: exit status 1</span><br><span class="line">[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-proxy:v1.15.1: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)</span><br><span class="line">, error: exit status 1</span><br><span class="line">[ERROR ImagePull]: failed to pull image k8s.gcr.io/pause:3.1: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)</span><br><span class="line">, error: exit status 1</span><br><span class="line">[ERROR ImagePull]: failed to pull image k8s.gcr.io/etcd:3.3.10: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)</span><br><span class="line">, error: exit status 1</span><br><span class="line">[ERROR ImagePull]: failed to pull image k8s.gcr.io/coredns:1.3.1: output: Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)</span><br><span class="line">, error: exit status 1</span><br></pre></td></tr></table></figure><p>好办，我们先找台海外服务器，把相应的镜像拉下来，推到我们自己的私有仓库里，再pull,然后改tag。没有私有仓库也不要紧，我已经把1.15.1推到hub.docker.com了。</p><p>找一台能连接k8s.gcr.io的服务器:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">docker pull k8s.gcr.io/kube-apiserver:v1.15.1</span><br><span class="line">docker pull k8s.gcr.io/kube-controller-manager:v1.15.1</span><br><span class="line">docker pull k8s.gcr.io/kube-scheduler:v1.15.1</span><br><span class="line">docker pull k8s.gcr.io/kube-proxy:v1.15.1</span><br><span class="line">docker pull k8s.gcr.io/pause:3.1</span><br><span class="line">docker pull k8s.gcr.io/etcd:3.3.10</span><br><span class="line">docker pull k8s.gcr.io/coredns:1.3.1</span><br><span class="line"></span><br><span class="line">docker tag k8s.gcr.io/kube-apiserver:v1.15.1 nas.pocketdigi.com:8083/k8s.gcr.io/kube-apiserver:v1.15.1</span><br><span class="line">docker tag k8s.gcr.io/kube-controller-manager:v1.15.1 nas.pocketdigi.com:8083/k8s.gcr.io/kube-controller-manager:v1.15.1</span><br><span class="line">docker tag k8s.gcr.io/kube-scheduler:v1.15.1 nas.pocketdigi.com:8083/k8s.gcr.io/kube-scheduler:v1.15.1</span><br><span class="line">docker tag k8s.gcr.io/kube-proxy:v1.15.1 nas.pocketdigi.com:8083/k8s.gcr.io/kube-proxy:v1.15.1</span><br><span class="line">docker tag k8s.gcr.io/pause:3.1 nas.pocketdigi.com:8083/k8s.gcr.io/pause:3.1</span><br><span class="line">docker tag k8s.gcr.io/etcd:3.3.10 nas.pocketdigi.com:8083/k8s.gcr.io/etcd:3.3.10</span><br><span class="line">docker tag k8s.gcr.io/coredns:1.3.1 nas.pocketdigi.com:8083/k8s.gcr.io/coredns:1.3.1</span><br><span class="line"></span><br><span class="line">docker push nas.pocketdigi.com:8083/k8s.gcr.io/kube-apiserver:v1.15.1</span><br><span class="line">docker push nas.pocketdigi.com:8083/k8s.gcr.io/kube-controller-manager:v1.15.1</span><br><span class="line">docker push nas.pocketdigi.com:8083/k8s.gcr.io/kube-scheduler:v1.15.1</span><br><span class="line">docker push nas.pocketdigi.com:8083/k8s.gcr.io/kube-proxy:v1.15.1</span><br><span class="line">docker push nas.pocketdigi.com:8083/k8s.gcr.io/pause:3.1</span><br><span class="line">docker push nas.pocketdigi.com:8083/k8s.gcr.io/etcd:3.3.10</span><br><span class="line">docker push nas.pocketdigi.com:8083/k8s.gcr.io/coredns:1.3.1</span><br></pre></td></tr></table></figure><p>回到安装kubernetes的机器:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">docker pull nas.pocketdigi.com:8083/k8s.gcr.io/kube-apiserver:v1.15.1</span><br><span class="line">docker pull nas.pocketdigi.com:8083/k8s.gcr.io/kube-controller-manager:v1.15.1</span><br><span class="line">docker pull nas.pocketdigi.com:8083/k8s.gcr.io/kube-scheduler:v1.15.1</span><br><span class="line">docker pull nas.pocketdigi.com:8083/k8s.gcr.io/kube-proxy:v1.15.1</span><br><span class="line">docker pull nas.pocketdigi.com:8083/k8s.gcr.io/pause:3.1</span><br><span class="line">docker pull nas.pocketdigi.com:8083/k8s.gcr.io/etcd:3.3.10</span><br><span class="line">docker pull nas.pocketdigi.com:8083/k8s.gcr.io/coredns:1.3.1</span><br><span class="line"></span><br><span class="line">docker tag nas.pocketdigi.com:8083/k8s.gcr.io/kube-apiserver:v1.15.1 k8s.gcr.io/kube-apiserver:v1.15.1</span><br><span class="line">docker tag nas.pocketdigi.com:8083/k8s.gcr.io/kube-controller-manager:v1.15.1 k8s.gcr.io/kube-controller-manager:v1.15.1</span><br><span class="line">docker tag nas.pocketdigi.com:8083/k8s.gcr.io/kube-scheduler:v1.15.1 k8s.gcr.io/kube-scheduler:v1.15.1 </span><br><span class="line">docker tag nas.pocketdigi.com:8083/k8s.gcr.io/kube-proxy:v1.15.1 k8s.gcr.io/kube-proxy:v1.15.1 </span><br><span class="line">docker tag nas.pocketdigi.com:8083/k8s.gcr.io/pause:3.1 k8s.gcr.io/pause:3.1 </span><br><span class="line">docker tag nas.pocketdigi.com:8083/k8s.gcr.io/etcd:3.3.10 k8s.gcr.io/etcd:3.3.10 </span><br><span class="line">docker tag nas.pocketdigi.com:8083/k8s.gcr.io/coredns:1.3.1 k8s.gcr.io/coredns:1.3.1</span><br></pre></td></tr></table></figure><p>没有条件的小伙伴，请直接使用下面的命令下载我转到hub.docker.com的镜像:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">docker pull pocketdigi/kube-apiserver:v1.15.1</span><br><span class="line">docker pull pocketdigi/kube-controller-manager:v1.15.1</span><br><span class="line">docker pull pocketdigi/kube-scheduler:v1.15.1</span><br><span class="line">docker pull pocketdigi/kube-proxy:v1.15.1</span><br><span class="line">docker pull pocketdigi/pause:3.1</span><br><span class="line">docker pull pocketdigi/etcd:3.3.10</span><br><span class="line">docker pull pocketdigi/coredns:1.3.1</span><br><span class="line"></span><br><span class="line">docker tag pocketdigi/kube-apiserver:v1.15.1 k8s.gcr.io/kube-apiserver:v1.15.1</span><br><span class="line">docker tag pocketdigi/kube-controller-manager:v1.15.1 k8s.gcr.io/kube-controller-manager:v1.15.1</span><br><span class="line">docker tag pocketdigi/kube-scheduler:v1.15.1 k8s.gcr.io/kube-scheduler:v1.15.1 </span><br><span class="line">docker tag pocketdigi/kube-proxy:v1.15.1 k8s.gcr.io/kube-proxy:v1.15.1 </span><br><span class="line">docker tag pocketdigi/pause:3.1 k8s.gcr.io/pause:3.1 </span><br><span class="line">docker tag pocketdigi/etcd:3.3.10 k8s.gcr.io/etcd:3.3.10 </span><br><span class="line">docker tag pocketdigi/coredns:1.3.1 k8s.gcr.io/coredns:1.3.1</span><br></pre></td></tr></table></figure><p>重新执行init:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubeadm init --apiserver-advertise-address=192.168.20.15 --pod-network-cidr=10.244.0.0/16 --apiserver-cert-extra-sans=nas.pocketdigi.com</span><br></pre></td></tr></table></figure><p>–pod-network-cidr是因为我们后面用的是flannel网络插件，默认就是这个网段</p><p>成功后得到以下信息:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">Your Kubernetes control-plane has initialized successfully!</span><br><span class="line"></span><br><span class="line">To start using your cluster, you need to run the following as a regular user:</span><br><span class="line"></span><br><span class="line">  mkdir -p $HOME/.kube</span><br><span class="line">  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config</span><br><span class="line">  sudo chown $(id -u):$(id -g) $HOME/.kube/config</span><br><span class="line"></span><br><span class="line">You should now deploy a pod network to the cluster.</span><br><span class="line">Run &quot;kubectl apply -f [podnetwork].yaml&quot; with one of the options listed at:</span><br><span class="line">  https://kubernetes.io/docs/concepts/cluster-administration/addons/</span><br><span class="line"></span><br><span class="line">Then you can join any number of worker nodes by running the following on each as root:</span><br><span class="line"></span><br><span class="line">kubeadm join 192.168.20.15:6443 --token m5sli6.w3z0zprk0883acuo \</span><br><span class="line">    --discovery-token-ca-cert-hash sha256:114fd2e850b62e7cb9924be9fb980d75e10e3e4c8e2505eddbfdd2e0f081b964</span><br></pre></td></tr></table></figure><p>注意保存token,token会在24小时后过期，到时如果需要往集群里加节点，需要重新创建token,discovery-token-ca-cert-hash值是不会变的</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubeadm token create</span><br></pre></td></tr></table></figure><p>复制kubectl所需的配置文件:</p><figure class="highlight plain"><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">mkdir -p $HOME/.kube</span><br><span class="line">sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config</span><br><span class="line">sudo chown $(id -u):$(id -g) $HOME/.kube/config</span><br></pre></td></tr></table></figure><p>安装网络插件flannel，先从镜像节点下载镜像</p><figure class="highlight plain"><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">docker pull pocketdigi/flannel:v0.11.0-amd64</span><br><span class="line">docker tag pocketdigi/flannel:v0.11.0-amd64 quay.io/coreos/flannel:v0.11.0-amd64</span><br><span class="line">kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml</span><br></pre></td></tr></table></figure><p>允许在master节点安装pod（默认不允许)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl taint nodes --all node-role.kubernetes.io/master-</span><br></pre></td></tr></table></figure><p>后期如果增加了节点，要禁止在master节点部署pod,通过以下命令恢复:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl taint nodes kubernetes-master  node-role.kubernetes.io/master=:NoSchedule</span><br></pre></td></tr></table></figure><p><code>kubernetes-master</code>是master节点名</p><p>如果使用外部安装的rancher管理集群，会无法注册，关闭防火墙解决:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl disable firewalld</span><br></pre></td></tr></table></figure><h2 id="Nginx-Ingress-Controller"><a href="#Nginx-Ingress-Controller" class="headerlink" title="Nginx Ingress Controller"></a>Nginx Ingress Controller</h2><p>如果要对外暴露http服务，建议安装Ingress. <a href="https://kubernetes.github.io/ingress-nginx/deploy/" target="_blank" rel="noopener">官方文档</a></p><p>官方文档里的yaml不会暴露80，443端口，需要下载后修改。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml</span><br></pre></td></tr></table></figure><p>在nginx-ingress-controller Deployment的spec.template.spec节点下增加<code>hostNetwork: true</code>，即使用主机网络。<br>如果有多台机器，不只一个master节点，需要把Deployment改成DaemonSet，以便在每个节点都部署nginx。最后修改后如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br></pre></td><td class="code"><pre><span class="line">apiVersion: v1</span><br><span class="line">kind: Namespace</span><br><span class="line">metadata:</span><br><span class="line">  name: ingress-nginx</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"></span><br><span class="line">kind: ConfigMap</span><br><span class="line">apiVersion: v1</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-configuration</span><br><span class="line">  namespace: ingress-nginx</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">kind: ConfigMap</span><br><span class="line">apiVersion: v1</span><br><span class="line">metadata:</span><br><span class="line">  name: tcp-services</span><br><span class="line">  namespace: ingress-nginx</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">kind: ConfigMap</span><br><span class="line">apiVersion: v1</span><br><span class="line">metadata:</span><br><span class="line">  name: udp-services</span><br><span class="line">  namespace: ingress-nginx</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</span><br><span class="line"></span><br><span class="line">---</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: nginx-ingress-serviceaccount</span><br><span class="line">  namespace: ingress-nginx</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1beta1</span><br><span class="line">kind: ClusterRole</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-ingress-clusterrole</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</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">      - configmaps</span><br><span class="line">      - endpoints</span><br><span class="line">      - nodes</span><br><span class="line">      - pods</span><br><span class="line">      - secrets</span><br><span class="line">    verbs:</span><br><span class="line">      - list</span><br><span class="line">      - watch</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</span><br><span class="line">    verbs:</span><br><span class="line">      - get</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - services</span><br><span class="line">    verbs:</span><br><span class="line">      - get</span><br><span class="line">      - list</span><br><span class="line">      - watch</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - events</span><br><span class="line">    verbs:</span><br><span class="line">      - create</span><br><span class="line">      - patch</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;extensions&quot;</span><br><span class="line">      - &quot;networking.k8s.io&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - ingresses</span><br><span class="line">    verbs:</span><br><span class="line">      - get</span><br><span class="line">      - list</span><br><span class="line">      - watch</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;extensions&quot;</span><br><span class="line">      - &quot;networking.k8s.io&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - ingresses/status</span><br><span class="line">    verbs:</span><br><span class="line">      - update</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1beta1</span><br><span class="line">kind: Role</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-ingress-role</span><br><span class="line">  namespace: ingress-nginx</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</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">      - configmaps</span><br><span class="line">      - pods</span><br><span class="line">      - secrets</span><br><span class="line">      - namespaces</span><br><span class="line">    verbs:</span><br><span class="line">      - get</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - configmaps</span><br><span class="line">    resourceNames:</span><br><span class="line">      # Defaults to &quot;&lt;election-id&gt;-&lt;ingress-class&gt;&quot;</span><br><span class="line">      # Here: &quot;&lt;ingress-controller-leader&gt;-&lt;nginx&gt;&quot;</span><br><span class="line">      # This has to be adapted if you change either parameter</span><br><span class="line">      # when launching the nginx-ingress-controller.</span><br><span class="line">      - &quot;ingress-controller-leader-nginx&quot;</span><br><span class="line">    verbs:</span><br><span class="line">      - get</span><br><span class="line">      - update</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - configmaps</span><br><span class="line">    verbs:</span><br><span class="line">      - create</span><br><span class="line">  - apiGroups:</span><br><span class="line">      - &quot;&quot;</span><br><span class="line">    resources:</span><br><span class="line">      - endpoints</span><br><span class="line">    verbs:</span><br><span class="line">      - get</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1beta1</span><br><span class="line">kind: RoleBinding</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-ingress-role-nisa-binding</span><br><span class="line">  namespace: ingress-nginx</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</span><br><span class="line">roleRef:</span><br><span class="line">  apiGroup: rbac.authorization.k8s.io</span><br><span class="line">  kind: Role</span><br><span class="line">  name: nginx-ingress-role</span><br><span class="line">subjects:</span><br><span class="line">  - kind: ServiceAccount</span><br><span class="line">    name: nginx-ingress-serviceaccount</span><br><span class="line">    namespace: ingress-nginx</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line">apiVersion: rbac.authorization.k8s.io/v1beta1</span><br><span class="line">kind: ClusterRoleBinding</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-ingress-clusterrole-nisa-binding</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</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: nginx-ingress-clusterrole</span><br><span class="line">subjects:</span><br><span class="line">  - kind: ServiceAccount</span><br><span class="line">    name: nginx-ingress-serviceaccount</span><br><span class="line">    namespace: ingress-nginx</span><br><span class="line"></span><br><span class="line">---</span><br><span class="line"></span><br><span class="line">apiVersion: apps/v1</span><br><span class="line">kind: DaemonSet</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx-ingress-controller</span><br><span class="line">  namespace: ingress-nginx</span><br><span class="line">  labels:</span><br><span class="line">    app.kubernetes.io/name: ingress-nginx</span><br><span class="line">    app.kubernetes.io/part-of: ingress-nginx</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.kubernetes.io/name: ingress-nginx</span><br><span class="line">      app.kubernetes.io/part-of: ingress-nginx</span><br><span class="line">  template:</span><br><span class="line">    metadata:</span><br><span class="line">      labels:</span><br><span class="line">        app.kubernetes.io/name: ingress-nginx</span><br><span class="line">        app.kubernetes.io/part-of: ingress-nginx</span><br><span class="line">      annotations:</span><br><span class="line">        prometheus.io/port: &quot;10254&quot;</span><br><span class="line">        prometheus.io/scrape: &quot;true&quot;</span><br><span class="line">    spec:</span><br><span class="line">      hostNetwork: true</span><br><span class="line">      serviceAccountName: nginx-ingress-serviceaccount</span><br><span class="line">      containers:</span><br><span class="line">        - name: nginx-ingress-controller</span><br><span class="line">          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0</span><br><span class="line">          args:</span><br><span class="line">            - /nginx-ingress-controller</span><br><span class="line">            - --configmap=$(POD_NAMESPACE)/nginx-configuration</span><br><span class="line">            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services</span><br><span class="line">            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services</span><br><span class="line">            - --publish-service=$(POD_NAMESPACE)/ingress-nginx</span><br><span class="line">            - --annotations-prefix=nginx.ingress.kubernetes.io</span><br><span class="line">          securityContext:</span><br><span class="line">            allowPrivilegeEscalation: true</span><br><span class="line">            capabilities:</span><br><span class="line">              drop:</span><br><span class="line">                - ALL</span><br><span class="line">              add:</span><br><span class="line">                - NET_BIND_SERVICE</span><br><span class="line">            # www-data -&gt; 33</span><br><span class="line">            runAsUser: 33</span><br><span class="line">          env:</span><br><span class="line">            - name: POD_NAME</span><br><span class="line">              valueFrom:</span><br><span class="line">                fieldRef:</span><br><span class="line">                  fieldPath: metadata.name</span><br><span class="line">            - name: POD_NAMESPACE</span><br><span class="line">              valueFrom:</span><br><span class="line">                fieldRef:</span><br><span class="line">                  fieldPath: metadata.namespace</span><br><span class="line">          ports:</span><br><span class="line">            - name: http</span><br><span class="line">              containerPort: 80</span><br><span class="line">            - name: https</span><br><span class="line">              containerPort: 443</span><br><span class="line">          livenessProbe:</span><br><span class="line">            failureThreshold: 3</span><br><span class="line">            httpGet:</span><br><span class="line">              path: /healthz</span><br><span class="line">              port: 10254</span><br><span class="line">              scheme: HTTP</span><br><span class="line">            initialDelaySeconds: 10</span><br><span class="line">            periodSeconds: 10</span><br><span class="line">            successThreshold: 1</span><br><span class="line">            timeoutSeconds: 10</span><br><span class="line">          readinessProbe:</span><br><span class="line">            failureThreshold: 3</span><br><span class="line">            httpGet:</span><br><span class="line">              path: /healthz</span><br><span class="line">              port: 10254</span><br><span class="line">              scheme: HTTP</span><br><span class="line">            periodSeconds: 10</span><br><span class="line">            successThreshold: 1</span><br><span class="line">            timeoutSeconds: 10</span><br><span class="line"></span><br><span class="line">---</span><br></pre></td></tr></table></figure><p>然后在master和worker节点pull镜像</p><figure class="highlight plain"><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">docker pull pocketdigi/nginx-ingress-controller:0.25.0</span><br><span class="line">docker tag pocketdigi/nginx-ingress-controller:0.25.0 quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.0</span><br></pre></td></tr></table></figure><p>在master节点执行:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f mandatory.yaml</span><br></pre></td></tr></table></figure><p>添加ingress-nginx Service,这一步不同主机提供商操作不同，参考官方文档，一般虚拟机就用下面的就可以了。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml</span><br></pre></td></tr></table></figure><p>导入SSL证书</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl create secret tls nas-pocketdigi-com --key private.pem  --cert fullchain.pem --namespace prod</span><br></pre></td></tr></table></figure><p><code>nas-pocketdigi-com</code>是证书名，我这个是泛域名证书,prod是指定导入的命名空间，需要先创建</p><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>nginx.yaml</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><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</span><br><span class="line">  namespace: prod</span><br><span class="line">spec:</span><br><span class="line">  selector:</span><br><span class="line">    app: nginx</span><br><span class="line">  type: ClusterIP</span><br><span class="line">  ports:</span><br><span class="line">  - name: default</span><br><span class="line">    port: 80</span><br><span class="line">    protocol: TCP</span><br><span class="line">    targetPort: 80</span><br><span class="line">---</span><br><span class="line">apiVersion: extensions/v1beta1</span><br><span class="line">kind: Ingress</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx</span><br><span class="line">  namespace: prod</span><br><span class="line">spec:</span><br><span class="line">  tls:</span><br><span class="line">    - hosts:</span><br><span class="line">      - nginx.nas.pocketdigi.com</span><br><span class="line">      secretName: nas-pocketdigi-com</span><br><span class="line">  rules:</span><br><span class="line">    - host: nginx.nas.pocketdigi.com</span><br><span class="line">      http:</span><br><span class="line">        paths:</span><br><span class="line">          - backend:</span><br><span class="line">              serviceName: nginx</span><br><span class="line">              servicePort: 80</span><br><span class="line">---</span><br><span class="line">apiVersion: apps/v1beta1</span><br><span class="line">kind: Deployment</span><br><span class="line">metadata:</span><br><span class="line">  name: nginx</span><br><span class="line">  namespace: prod</span><br><span class="line">spec:</span><br><span class="line">  progressDeadlineSeconds: 180</span><br><span class="line">  strategy:</span><br><span class="line">    rollingUpdate:</span><br><span class="line">      maxSurge: 1</span><br><span class="line">      maxUnavailable: 0</span><br><span class="line">    type: RollingUpdate</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</span><br><span class="line">    spec:</span><br><span class="line">      containers:</span><br><span class="line">        - name: nginx</span><br><span class="line">          imagePullPolicy: Always</span><br><span class="line">          image: nginx:1.17.1-alpine</span><br><span class="line">          resources:</span><br><span class="line">            requests:</span><br><span class="line">              memory: &quot;256Mi&quot;</span><br><span class="line">              cpu: &quot;0.1&quot;</span><br><span class="line">            limits:</span><br><span class="line">              memory: &quot;384Mi&quot;</span><br><span class="line">              cpu: &quot;0.8&quot;</span><br><span class="line">          livenessProbe:</span><br><span class="line">            failureThreshold: 3</span><br><span class="line">            httpGet:</span><br><span class="line">              path: /</span><br><span class="line">              port: 80</span><br><span class="line">              scheme: HTTP</span><br><span class="line">            initialDelaySeconds: 120</span><br><span class="line">            periodSeconds: 20</span><br><span class="line">            successThreshold: 1</span><br><span class="line">            timeoutSeconds: 2</span><br><span class="line">          readinessProbe:</span><br><span class="line">            failureThreshold: 3</span><br><span class="line">            httpGet:</span><br><span class="line">              path: /</span><br><span class="line">              port: 80</span><br><span class="line">              scheme: HTTP</span><br><span class="line">            initialDelaySeconds: 60</span><br><span class="line">            periodSeconds: 20</span><br><span class="line">            successThreshold: 2</span><br><span class="line">            timeoutSeconds: 3</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">kubectl apply -f nginx.yaml</span><br></pre></td></tr></table></figure><p>如果nginx.nas.pocketdigi.com已经解析到这台主机，现在应该能正常访问到nginx。</p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;环境&quot;&gt;&lt;a href=&quot;#环境&quot; class=&quot;headerlink&quot; title=&quot;环境&quot;&gt;&lt;/a&gt;环境&lt;/h2&gt;&lt;p&gt; 操作系统: CentOS Linux release 7.6.1810&lt;/p&gt;
&lt;p&gt; 内网ip: 192.168.20.15&lt;/p&gt;
&lt;p&gt; kubernetes: v1.15.1&lt;/p&gt;
&lt;p&gt; docker: 19.03.1&lt;/p&gt;
&lt;h2 id=&quot;安装docker&quot;&gt;&lt;a href=&quot;#安装docker&quot; class=&quot;headerlink&quot; title=&quot;安装docker&quot;&gt;&lt;/a&gt;安装docker&lt;/h2&gt; &lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;curl -fsSL get.docker.com -o get-docker.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;sh get-docker.sh --mirror Aliyun&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;systemctl enable docker&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;systemctl start docker&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="Other" scheme="http://www.pocketdigi.com/categories/Other/"/>
    
    
  </entry>
  
  <entry>
    <title>博客迁移到Hexo</title>
    <link href="http://www.pocketdigi.com/2019-09/blog-migration.html"/>
    <id>http://www.pocketdigi.com/2019-09/blog-migration.html</id>
    <published>2019-09-02T08:34:52.000Z</published>
    <updated>2019-09-02T08:45:16.916Z</updated>
    
    <content type="html"><![CDATA[<p>忍不了wordpress的缓慢，没找到一个完美支持markdown，又看得上的主题，是这次迁移的原因。按官方文档迁移后，现在的效果是文章基本都没问题，但是类目，tag之类的链接全变了，所以会有很多404，但还好，反正博客访问量也不大。静态网站，速度贼快，哈哈。</p><p>编辑器，我现在用的是Visual Studio Code,图片用PicGo插件，配上阿里云oss，非常方便。部署用的是hexo-deployer-rsync插件，一条命令，打算写个小脚本，配上gitlab runner,实现提交后自动部署。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;忍不了wordpress的缓慢，没找到一个完美支持markdown，又看得上的主题，是这次迁移的原因。按官方文档迁移后，现在的效果是文章基本都没问题，但是类目，tag之类的链接全变了，所以会有很多404，但还好，反正博客访问量也不大。静态网站，速度贼快，哈哈。&lt;/p&gt;
&lt;p
      
    
    </summary>
    
      <category term="Other" scheme="http://www.pocketdigi.com/categories/Other/"/>
    
    
  </entry>
  
  <entry>
    <title>Spring cloud使用zookeeper作为分布式配置中心</title>
    <link href="http://www.pocketdigi.com/2019-08/20/spring-cloud-zookeeper-config.html"/>
    <id>http://www.pocketdigi.com/2019-08/20/spring-cloud-zookeeper-config.html</id>
    <published>2019-08-20T05:07:37.000Z</published>
    <updated>2019-09-02T08:40:16.397Z</updated>
    
    <content type="html"><![CDATA[<h3 id="为什么要用配置中心"><a href="#为什么要用配置中心" class="headerlink" title="为什么要用配置中心"></a>为什么要用配置中心</h3><p>Spring boot项目在启动时会加载application.properties(或yaml)里的配置，如果修改了application.properties，需要重新打包、部署，当服务有多个实例，需要每个都重新部署。配置中心就是用来解决这个问题的，我们所配置内容放在配置中心统一管理，项目启动时先去配置中心拉取配置，再用这些配置执行启动操作。如果配置有变更，在配置中心修改后，只需要一一重启服务，即可完成配置更新。有些配置中心甚至不需要重启，支持配置推送，比如我们今天介绍的Spring cloud zookeeper config。</p><h3 id="Spring-Cloud-Zookeeper-Config-介绍"><a href="#Spring-Cloud-Zookeeper-Config-介绍" class="headerlink" title="Spring Cloud Zookeeper Config 介绍"></a>Spring Cloud Zookeeper Config 介绍</h3><p>Zookeeper提供了一个类似目录树的存储结构，允许客户端存储任意数据，如：配置数据。</p><p>Spring Cloud Zookeeper Config是Spring Cloud Config Server和Client的替代方案(这个方案需要起一个server服务，依赖git存储配置)，在<code>bootstrap</code>阶段，加载配置到Spring环境中。</p><p>默认情况下，配置存储在<code>/config</code>节点下。Spring Cloud Zookeeper Config会基于应用程序名称和profile创建多个<code>PropertySource</code>模拟Spring Cloud Config顺序从Zookeeper读取并解析配置。</p><p>例如，应用名是<code>testApp</code>,profile是<code>dev</code>会创建下面几个property源：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">/config/testApp,dev</span><br><span class="line">/config/testApp</span><br><span class="line">/config/application,dev</span><br><span class="line">/config/application</span><br></pre></td></tr></table></figure><p>配置优先级是从上至下的。<code>/config/application</code> 会应用到所有使用zookeeper作配置中心的项目上，<code>/config/testApp</code> 只对应用名为<code>testApp</code>的项目有效.</p><a id="more"></a><h3 id="如何配置Spring-Cloud-Zookeeper-Config"><a href="#如何配置Spring-Cloud-Zookeeper-Config" class="headerlink" title="如何配置Spring Cloud Zookeeper Config"></a>如何配置Spring Cloud Zookeeper Config</h3><p>下面的例子是</p><h4 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h4><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></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-zookeeper-config<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>2.1.0.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>版本号跟你用的spring boot版本相关，2.1.x版本可以直接使用2.1.0.RELEASE，其他版本参考 <a href="https://spring.io/projects/spring-cloud" target="_blank" rel="noopener">https://spring.io/projects/spring-cloud</a></p><h4 id="修改默认配置"><a href="#修改默认配置" class="headerlink" title="修改默认配置"></a>修改默认配置</h4><p>上文提到Spring cloud zookeeper config默认配置存在<code>/confi</code>节点，默认读取<code>/config/application</code>配置，application和profile之间用逗号分割，这些都是可配置的。</p><p>resources目录下创建bootstrap.properties：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">spring.cloud.zookeeper.config.enable=true</span><br><span class="line">spring.cloud.zookeeper.config.root=config</span><br><span class="line">spring.cloud.zookeeper.config.defaultContext=application</span><br><span class="line">spring.cloud.zookeeper.config.profileSeparator=,</span><br></pre></td></tr></table></figure><p>不过没有特殊需要，不建议修改这些默认配置，原因下面会讲。</p><h4 id="引用配置"><a href="#引用配置" class="headerlink" title="引用配置"></a>引用配置</h4><p>引用配置方法跟放properties里的一样，用@Value或者 @ConfigurationProperties,只有在使用@ConfigurationProperties才能动态更新, @Value要重启后生效</p><figure class="highlight plain"><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">@Value(&quot;$&#123;key&#125;&quot;)</span><br><span class="line">String key;</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">@ConfigurationProperties(prefix = &quot;test&quot;)</span><br><span class="line">public class Test &#123;</span><br><span class="line">    String key1;</span><br><span class="line">    String key2;</span><br><span class="line"></span><br><span class="line">    public String getKey1() &#123;</span><br><span class="line">        return key1;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void setKey1(String key1) &#123;</span><br><span class="line">        this.key1 = key1;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public String getKey2() &#123;</span><br><span class="line">        return key2;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void setKey2(String key2) &#123;</span><br><span class="line">        this.key2 = key2;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="图形化配置"><a href="#图形化配置" class="headerlink" title="图形化配置:"></a>图形化配置:</h4><p>Spring并没有提供一个图形化修改配置的工具，但有第三方解决方案。<a href="https://github.com/pocketdigi/zookeeper-config-keeper" target="_blank" rel="noopener">zookeeper-config-keeper</a></p><p>docker一键部署:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run  --name=zkck -p 80:80 -e jwtsecret=asdfwefasdf -e zookeeperaddress=192.168.2.103:2181 pocketdigi/zookeeper-config-keeper:v0.1</span><br></pre></td></tr></table></figure><p><img src="https://blog-img.pocketdigi.com/file/2019-9-2/20190902162610.png" alt="2019-9-2/20190902162610.png"></p>]]></content>
    
    <summary type="html">
    
      &lt;h3 id=&quot;为什么要用配置中心&quot;&gt;&lt;a href=&quot;#为什么要用配置中心&quot; class=&quot;headerlink&quot; title=&quot;为什么要用配置中心&quot;&gt;&lt;/a&gt;为什么要用配置中心&lt;/h3&gt;&lt;p&gt;Spring boot项目在启动时会加载application.properties(或yaml)里的配置，如果修改了application.properties，需要重新打包、部署，当服务有多个实例，需要每个都重新部署。配置中心就是用来解决这个问题的，我们所配置内容放在配置中心统一管理，项目启动时先去配置中心拉取配置，再用这些配置执行启动操作。如果配置有变更，在配置中心修改后，只需要一一重启服务，即可完成配置更新。有些配置中心甚至不需要重启，支持配置推送，比如我们今天介绍的Spring cloud zookeeper config。&lt;/p&gt;
&lt;h3 id=&quot;Spring-Cloud-Zookeeper-Config-介绍&quot;&gt;&lt;a href=&quot;#Spring-Cloud-Zookeeper-Config-介绍&quot; class=&quot;headerlink&quot; title=&quot;Spring Cloud Zookeeper Config 介绍&quot;&gt;&lt;/a&gt;Spring Cloud Zookeeper Config 介绍&lt;/h3&gt;&lt;p&gt;Zookeeper提供了一个类似目录树的存储结构，允许客户端存储任意数据，如：配置数据。&lt;/p&gt;
&lt;p&gt;Spring Cloud Zookeeper Config是Spring Cloud Config Server和Client的替代方案(这个方案需要起一个server服务，依赖git存储配置)，在&lt;code&gt;bootstrap&lt;/code&gt;阶段，加载配置到Spring环境中。&lt;/p&gt;
&lt;p&gt;默认情况下，配置存储在&lt;code&gt;/config&lt;/code&gt;节点下。Spring Cloud Zookeeper Config会基于应用程序名称和profile创建多个&lt;code&gt;PropertySource&lt;/code&gt;模拟Spring Cloud Config顺序从Zookeeper读取并解析配置。&lt;/p&gt;
&lt;p&gt;例如，应用名是&lt;code&gt;testApp&lt;/code&gt;,profile是&lt;code&gt;dev&lt;/code&gt;会创建下面几个property源：&lt;/p&gt;
&lt;figure class=&quot;highlight plain&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;/config/testApp,dev&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/config/testApp&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/config/application,dev&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;/config/application&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;

&lt;p&gt;配置优先级是从上至下的。&lt;code&gt;/config/application&lt;/code&gt; 会应用到所有使用zookeeper作配置中心的项目上，&lt;code&gt;/config/testApp&lt;/code&gt; 只对应用名为&lt;code&gt;testApp&lt;/code&gt;的项目有效.&lt;/p&gt;
    
    </summary>
    
      <category term="Java" scheme="http://www.pocketdigi.com/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>Kong http 301跳转到https插件</title>
    <link href="http://www.pocketdigi.com/20190627/1647.html"/>
    <id>http://www.pocketdigi.com/20190627/1647.html</id>
    <published>2019-06-27T12:41:23.000Z</published>
    <updated>2019-08-20T08:11:05.834Z</updated>
    
    <content type="html"><![CDATA[<p>安全起见，我们希望用户使用https协议访问网站，但是在不输协议的时候，浏览器默认使用的是http协议。如果之前使用的是http协议，搜索引擎也会索引http协议的链接，用户点击打开的也是http协议。 出于对搜索引擎友好起见，这种场景我们一般使用301重定向，告诉搜索引擎已经永久转移到一个新地址，以后索引新地址。 之前我的博客一直存在这个问题，前端用kong,处理wordpress加了个nginx,kong和nginx之间http协议通讯。之前没有kong的时候，在nginx上配置重定向所有http到https,但是加了kong,不能这么做了，因为不管用户访问kong用的是http还是https，到nginx都是http。 今天抽了点时间写了个kong插件，完美解决这个问题，欢迎fork <a href="https://github.com/pocketdigi/kong-plugin-http301https" target="_blank" rel="noopener">kong-plugin-http301https</a>。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;安全起见，我们希望用户使用https协议访问网站，但是在不输协议的时候，浏览器默认使用的是http协议。如果之前使用的是http协议，搜索引擎也会索引http协议的链接，用户点击打开的也是http协议。 出于对搜索引擎友好起见，这种场景我们一般使用301重定向，告诉搜索引擎
      
    
    </summary>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Linux/"/>
    
    
  </entry>
  
  <entry>
    <title>kong admin ui  Custom Headers使用说明 给你的kong加个密码</title>
    <link href="http://www.pocketdigi.com/20190613/1644.html"/>
    <id>http://www.pocketdigi.com/20190613/1644.html</id>
    <published>2019-06-13T02:34:52.000Z</published>
    <updated>2019-09-02T08:26:57.022Z</updated>
    
    <content type="html"><![CDATA[<p>Kong默认的管理接口，未加任何安全校验，如果暴露在公网，随时可能被坏人发现，随意修改我们的配置。 <a href="https://github.com/pocketdigi/kong-admin-ui" target="_blank" rel="noopener">Kong admin ui</a>本身是个纯前端项目，没有额外的安全措施来保护您的Kong，未来也不可能加上后端代码实现账户授权。 Custom Headers功能，就是为了解决安全问题而开发的。</p><a id="more"></a><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>Kong本身自带授权认证插件，如<a href="https://docs.konghq.com/hub/kong-inc/basic-auth/" target="_blank" rel="noopener">Basic Authentication</a>,我们通过kong代理kong admin api，再配上Basic Authentication插件，访问kong admin api时，需要带上账号密码。根据Basic Authentication规范，账号密码就是base64编码后，通过header里的Authorization字段传输。而Custom Headers功能会在给Kong admin api接口发送请求时，加上您指定的header，所以可以实现带密码访问kong admin api,解决了安全问题。</p><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><p>假设部署kong的部署地址是 <a href="http://192.168.0.205/" target="_blank" rel="noopener">http://192.168.0.205/</a>, admin api地址是 <a href="http://192.168.0.205:8001/" target="_blank" rel="noopener">http://192.168.0.205:8001/</a> ，我们接下来把<a href="http://192.168.0.205/manage" target="_blank" rel="noopener">http://192.168.0.205/manage</a> 转到 <a href="http://192.168.0.205:8001/" target="_blank" rel="noopener">http://192.168.0.205:8001/</a> ,并配上Basic Authentication.</p><h4 id="Service和Route配置"><a href="#Service和Route配置" class="headerlink" title="Service和Route配置"></a>Service和Route配置</h4><p>我们先使用<a href="http://192.168.0.205:8001/" target="_blank" rel="noopener">http://192.168.0.205:8001/</a> 配置，完成后再禁止admin api外部访问 <img src="http://kong-admin.pocketdigi.com/images/1.png" alt="image"> 创建Service: <img src="http://kong-admin.pocketdigi.com/images/2.png" alt="image"> 创建Route: <img src="http://kong-admin.pocketdigi.com/images/3.png" alt="image"> 完成上面操作后，可以试试浏览器打开 <a href="http://192.168.0.205/manage" target="_blank" rel="noopener">http://192.168.0.205/manage</a> ，可以访问admin api了。</p><h4 id="Consumer-配置"><a href="#Consumer-配置" class="headerlink" title="Consumer 配置"></a>Consumer 配置</h4><p>Basic Authentication 需要Credential才可以访问，Credential属于某个Consumer,一个Consumer可以有多个Credential。 先创建 Consumer <img src="http://kong-admin.pocketdigi.com/images/4.png" alt="image"> 再创建一个Basic Auth Credential，输入账号密码，这个账号密码就是Basic Authentication需要的。 <img src="http://kong-admin.pocketdigi.com/images/5.png" alt="image"></p><h4 id="Basic-Authentication-配置"><a href="#Basic-Authentication-配置" class="headerlink" title="Basic Authentication 配置"></a>Basic Authentication 配置</h4><p><img src="http://kong-admin.pocketdigi.com/images/6.png" alt="image"> 完成配置以后，再用浏览器打开 <a href="http://192.168.0.205/manage" target="_blank" rel="noopener">http://192.168.0.205/manage</a> 提示输入账号密码，刚刚我们配的都是admin。 Basic Authentication插件会校验header里的Authorization字段，值的格式是<code>Basic base64(账号:密码)</code> <code>admin:admin</code> base64以后得到的值是<code>YWRtaW46YWRtaW4=</code>,所以在Kong Admin UI里使用，Custom Headers字段应该填 <code>{&quot;Authorization&quot;:&quot;Basic YWRtaW46YWRtaW4=&quot;}</code> <img src="http://kong-admin.pocketdigi.com/images/7.png" alt="image"> 如果其他api没有用到Consumer，配置就到此结束了，接下来配置改kong的配置文件，限制本机外的ip无法访问admin api即可。但如果其他服务也有Consumer，那么需要用ACL限制一下指定的Consumer才能访问admin api,因为默认情况下，所有Consumer都能访问。</p><h4 id="ACL配置"><a href="#ACL配置" class="headerlink" title="ACL配置"></a>ACL配置</h4><p>ACL黑白名单配置的是Consumer group,所以需要先把我们指定的Consumer加到一个group里。 <img src="http://kong-admin.pocketdigi.com/images/8.png" alt="image"> ACL Group这个功能是我在写这篇教程时加的，老版本的用户要更新下。 再配置ACL: <img src="http://kong-admin.pocketdigi.com/images/9.png" alt="image"> config.whitelist输入group. 现在，只有指定admin可以访问了。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Kong默认的管理接口，未加任何安全校验，如果暴露在公网，随时可能被坏人发现，随意修改我们的配置。 &lt;a href=&quot;https://github.com/pocketdigi/kong-admin-ui&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Kong admin ui&lt;/a&gt;本身是个纯前端项目，没有额外的安全措施来保护您的Kong，未来也不可能加上后端代码实现账户授权。 Custom Headers功能，就是为了解决安全问题而开发的。&lt;/p&gt;
    
    </summary>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Linux/"/>
    
    
  </entry>
  
  <entry>
    <title>Kong网关可视化管理界面 kong admin ui</title>
    <link href="http://www.pocketdigi.com/20190111/1637.html"/>
    <id>http://www.pocketdigi.com/20190111/1637.html</id>
    <published>2019-01-11T06:27:49.000Z</published>
    <updated>2019-08-20T08:11:06.528Z</updated>
    
    <content type="html"><![CDATA[<p>这段时间在学习kong,用postman或是curl操作kong admin api始终不太方便.有个开源项目叫kong dashboard，但貌似不支持最新版的kong。 于是自己用Vue和iView写了一个简单的管理界面，目前只支持Service,Route,Upstream,Target的增删改查，后续会添加更多功能。项目已托管到github: <a href="https://github.com/pocketdigi/kong-admin-ui" target="_blank" rel="noopener">https://github.com/pocketdigi/kong-admin-ui</a> 纯前端项目，所有对Kong admin api的操作都是浏览器发出的，所以，要求您的网络环境可以直接访问kong admin api。这么设计是想方便大家，不用再单独部署一套Kong的UI,如果你只是想找个管理界面，不想对这个项目进行二次开发，那么，只需要使用我部署的 <a href="http://kong-admin.pocketdigi.com" target="_blank" rel="noopener">demo</a>。除了你的kong admin api接口，不会再请求其他接口，所以你可以放心，我不会保存你的任何信息。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;这段时间在学习kong,用postman或是curl操作kong admin api始终不太方便.有个开源项目叫kong dashboard，但貌似不支持最新版的kong。 于是自己用Vue和iView写了一个简单的管理界面，目前只支持Service,Route,Upstr
      
    
    </summary>
    
      <category term="前端开发" scheme="http://www.pocketdigi.com/categories/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    
  </entry>
  
  <entry>
    <title>Kong 网关中文文档</title>
    <link href="http://www.pocketdigi.com/20181028/1631.html"/>
    <id>http://www.pocketdigi.com/20181028/1631.html</id>
    <published>2018-10-28T06:32:55.000Z</published>
    <updated>2019-08-20T08:11:05.935Z</updated>
    
    <content type="html"><![CDATA[<p>最近在学习Kong,一个基于Nginx的Api网关,发现网上还没有中文文档，所以打算每天抽点时间，把官方文档翻译一下，方便对Kong感兴趣的国内开发者，目前翻译工作刚刚开始，持续更新。文档链接 <a href="https://www.pocketdigi.com/book/kong/">Kong网关中文文档</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近在学习Kong,一个基于Nginx的Api网关,发现网上还没有中文文档，所以打算每天抽点时间，把官方文档翻译一下，方便对Kong感兴趣的国内开发者，目前翻译工作刚刚开始，持续更新。文档链接 &lt;a href=&quot;https://www.pocketdigi.com/book
      
    
    </summary>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Linux/"/>
    
    
  </entry>
  
  <entry>
    <title>openwrt路由器(小米路由)实现自定义DDNS(动态域名解析）</title>
    <link href="http://www.pocketdigi.com/20181009/1626.html"/>
    <id>http://www.pocketdigi.com/20181009/1626.html</id>
    <published>2018-10-09T06:23:54.000Z</published>
    <updated>2019-08-20T08:11:04.866Z</updated>
    
    <content type="html"><![CDATA[<p>大部分路由器（包括非智能路由)都支持ddns,一般是集成花生壳、公云等有限的几家服务商。直接使用路由器集成的ddns功能，优点是简单，缺点就是慢。因为DDNS都是用服务商提供的域名，如果要使用我们自己的域名，需要cname到服务商的域名，这里就存在二次解析，更别说免费DDNS本身速度慢了。 现在的智能路由器都是基于linux的，我们完全可以写一个脚本检测外网ip,在路由器外网ip变化时，去DNS服务提供商直接更改dns设置新的IP,实现DDNS功能。 准备环境:</p><ul><li>路由器:小米路由3G 需root，否则登不上ssh, ip 192.168.0.1</li><li>DNS服务商：dnspod</li></ul><p>理论上所有openwrt路由都支持，因为下面的脚本没用到小米路由的特性，都是linux上的命令，但我没有测其他路由器。 dnspod开放了api，可以调接口更新记录，现在已经被腾讯收购，也可以用腾讯的api,但腾讯的api比较复杂，反正我没调通。其他的像阿里云也开放了云解析接口，有需要的同学可以自己研究。 重点不在脚本，而在于思路：</p><ul><li>linux 定时任务，每分钟执行一次脚本</li><li>脚本访问外网指定服务器，获取当前外网ip地址，比较上次获取的外网ip地址，如果不一致，则调dns系统的api更新记录</li></ul><p>ddns 脚本内容:</p><pre><code>#!/bin/sholdIPFile=/tmp/oldip.txtlogin_token=xxxxxxxdomain=pocketdigi.comrecord_id=xxxxxxxsub_domain=testupdateIp(){  result=$(curl -s -d &quot;login_token=$login_token&amp;format=json&amp;domain=$domain&amp;record_id=$record_id&amp;sub_domain=$sub_domain&amp;value=$myip&amp;record_type=A&amp;record_line=默认&quot; https://dnsapi.cn/Record.Modify)  grepResult=$(echo $result | grep &quot;\&quot;code\&quot;:\&quot;1\&quot;&quot;)  if [[ &quot;$grepResult&quot; != &quot;&quot; ]]   then       echo &apos;更新成功&apos;       echo &quot;$myip&quot; &gt; $oldIPFile   else       echo &apos;更新失败&apos;  fi}myip=$(curl -s http://myip.dnsomatic.com/)echo &quot;当前ip:$myip&quot;if [ ! -f &quot;$oldIPFile&quot; ]; then  echo &quot;文件不存在，更新&quot;  updateIp;else  oldip=$(cat $oldIPFile)  echo &quot;旧IP:$oldip&quot;  if [ &quot;$myip&quot; = &quot;$oldip&quot; ]; then    echo &quot;当前IP与旧IP相同，不更新&quot;  else    echo &quot;当前IP与旧IP不同，更新&quot;    updateIp  fifi</code></pre><p>login_token需要登录dnspod获取，record_id可以使用chrome,在dnspod后台编辑保存那条记录时抓包找到。 使用scp将脚本拷到路由器上的/data目录，小米路由很多目录是只读的，写不进去 ssh登录路由器:</p><pre><code>ssh root@192.168.0.1</code></pre><p>密码需要到小米路由官网找 给ddns脚本增加可执行权限</p><pre><code>chmod +x /data/ddns</code></pre><p>添加定时任务</p><pre><code>crontab -e</code></pre><p>在末尾添加</p><pre><code>* * * * * /data/ddns</code></pre><p>大功告成！</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;大部分路由器（包括非智能路由)都支持ddns,一般是集成花生壳、公云等有限的几家服务商。直接使用路由器集成的ddns功能，优点是简单，缺点就是慢。因为DDNS都是用服务商提供的域名，如果要使用我们自己的域名，需要cname到服务商的域名，这里就存在二次解析，更别说免费DDN
      
    
    </summary>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Linux/"/>
    
    
  </entry>
  
  <entry>
    <title>Kibana 使用Google,高德地图</title>
    <link href="http://www.pocketdigi.com/20180828/1621.html"/>
    <id>http://www.pocketdigi.com/20180828/1621.html</id>
    <published>2018-08-28T09:28:42.000Z</published>
    <updated>2019-08-20T08:11:05.936Z</updated>
    
    <content type="html"><![CDATA[<p>kibana默认的地图大部分都是英文，对国人不太友好。Kibana使用的是tilemap瓦片地图，支持配置其他地图，其标准与google的tilemap相同，采用这一标准的还有高德。所以，我们可以直接换成google或高德。 打开config下的kibana.yml,在文件底部添加:</p><pre><code>tilemap.url: &apos;http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&amp;size=1&amp;scale=1&amp;style=7&amp;x={x}&amp;y={y}&amp;z={z}&apos;#tilemap.url: &apos;http://mt2.google.cn/vt/lyrs=m&amp;hl=zh-CN&amp;gl=cn&amp;x={x}&amp;y={y}&amp;z={z}&apos;tilemap.options.maxZoom: 18</code></pre><p>用google还是高德随意，google.cn在国内还是可以访问的，<code>tilemap.options.maxZoom</code>配置的是最大缩放级别，默认只能缩放到区级。 重启kibana，6.3.1实测有效。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;kibana默认的地图大部分都是英文，对国人不太友好。Kibana使用的是tilemap瓦片地图，支持配置其他地图，其标准与google的tilemap相同，采用这一标准的还有高德。所以，我们可以直接换成google或高德。 打开config下的kibana.yml,在文件
      
    
    </summary>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Linux/"/>
    
    
  </entry>
  
  <entry>
    <title>Elasticsearch 用systemctl启动报max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]</title>
    <link href="http://www.pocketdigi.com/20180707/1618.html"/>
    <id>http://www.pocketdigi.com/20180707/1618.html</id>
    <published>2018-07-07T03:21:24.000Z</published>
    <updated>2019-08-20T08:11:06.531Z</updated>
    
    <content type="html"><![CDATA[<p>已经修改了/etc/security/limits.conf ,把数值加到65536，用elasticsearch用户启动，一切正常，但一旦用systemctl启动，还是报<code>max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]</code> 而且，我的配置文件已经指定使用elasticsearch用户，诡异的错误。 其实出现这个问题，原因在于/etc/security/limits.conf里的配置只针对PAM认证登录用户有效，而Systemd有自己的一套配置。全局配置在/etc/systemd/system.conf和/etc/systemd/user.conf,也可以对单个服务做配置，所以，elasticsearch的脚本要做下修改:</p><pre><code>[Unit]Description=Elasticsearch[Service]Environment=JAVA_HOME=/usr/local/jdk1.8.0_171LimitCORE=infinityLimitNOFILE=65536LimitNPROC=65536ExecStart=/usr/local/elasticsearch-6.3.1/bin/elasticsearchUser=elasticsearchGroup=elasticsearch[Install]WantedBy=multi-user.target</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;已经修改了/etc/security/limits.conf ,把数值加到65536，用elasticsearch用户启动，一切正常，但一旦用systemctl启动，还是报&lt;code&gt;max file descriptors [4096] for elasticsearch
      
    
    </summary>
    
      <category term="Java" scheme="http://www.pocketdigi.com/categories/Java/"/>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Java/Linux/"/>
    
    
  </entry>
  
  <entry>
    <title>Android Dialog弹出时不显示导航栏(沉浸式)</title>
    <link href="http://www.pocketdigi.com/20180704/1617.html"/>
    <id>http://www.pocketdigi.com/20180704/1617.html</id>
    <published>2018-07-04T07:54:51.000Z</published>
    <updated>2019-08-20T08:11:05.218Z</updated>
    
    <content type="html"><![CDATA[<p>我们的app是全屏沉浸式的，发现activity在设置了全屏后，弹出dialog底部会跳出导航栏虚拟键。具体原因是因为Dialog的Window抢走了焦点，Window 中的 DecorView 状态改变导致了退出。 解决方法，在Dialog中：</p><pre><code>private void fullScreenImmersive(View view) {     if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.KITKAT) {         int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE             | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION             | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION             | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY             | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN             | View.SYSTEM_UI_FLAG_FULLSCREEN;         view.setSystemUiVisibility(uiOptions);     } } @Override public void show() {     this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);     super.show();     fullScreenImmersive(getWindow().getDecorView());     this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); }</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;我们的app是全屏沉浸式的，发现activity在设置了全屏后，弹出dialog底部会跳出导航栏虚拟键。具体原因是因为Dialog的Window抢走了焦点，Window 中的 DecorView 状态改变导致了退出。 解决方法，在Dialog中：&lt;/p&gt;
&lt;pre&gt;&lt;cod
      
    
    </summary>
    
      <category term="Android开发" scheme="http://www.pocketdigi.com/categories/Android%E5%BC%80%E5%8F%91/"/>
    
    
  </entry>
  
  <entry>
    <title>Docker私服Sonatype Nexus的搭建与使用</title>
    <link href="http://www.pocketdigi.com/20180602/1613.html"/>
    <id>http://www.pocketdigi.com/20180602/1613.html</id>
    <published>2018-06-02T03:14:53.000Z</published>
    <updated>2019-08-20T08:11:05.218Z</updated>
    
    <content type="html"><![CDATA[<h4 id="为什么要建docker私服"><a href="#为什么要建docker私服" class="headerlink" title="为什么要建docker私服"></a>为什么要建docker私服</h4><ul><li>官服在国外，速度慢</li><li>创建的镜像是私有的，不想公开传到外部服务器上</li></ul><h4 id="Sonatype-Nexus介绍"><a href="#Sonatype-Nexus介绍" class="headerlink" title="Sonatype Nexus介绍"></a>Sonatype Nexus介绍</h4><p>Sonatype Nexus是一个仓库管理软件，支持Maven、Docker、Bower、PyPI、Yum等类型仓库。使用Maven管理依赖的Java程序员一定不陌生，搭建Maven私服基本是用这个。注意:3.x版本才支持docker。 今天我们就用Nexus作Docker的私服，管理我们自己的私有镜像。</p><h4 id="Sonatype-Nexus安装"><a href="#Sonatype-Nexus安装" class="headerlink" title="Sonatype Nexus安装"></a>Sonatype Nexus安装</h4><p>很简单，下载，解压，我的路径是<br>/usr/local/nexus-3.11.0-01<br>创建开机启动脚本<br><code>vim /etc/systemd/system/nexus.service</code><br>内容如下:</p><pre><code>[Unit]Description=nexus serviceAfter=network.target[Service]Type=forkingLimitNOFILE=65536ExecStart=/usr/local/nexus-3.11.0-01/bin/nexus startExecStop=/usr/local/nexus-3.11.0-01/bin/nexus stopRestart=on-abort[Install]WantedBy=multi-user.target</code></pre><p>设置开机启动<br><code>systemctl enable nexus</code><br>启动<br><code>systemctl start nexus</code> nexus默认端口是8081, 现在打开浏览器，访问<a href="http://ip:8081" target="_blank" rel="noopener">http://ip:8081</a>, 应该能看到如下界面<br><img src="https://blog-img.pocketdigi.com/file/2018/06/15279101909906.jpg" alt>￼ 点击右上角Sign in,账号admin 密码admin123,登录<br><img src="https://blog-img.pocketdigi.com/file/2018/06/15279103236430.jpg" alt>￼ 点击admin ,把默认的密码改掉，安装完成。</p><h4 id="创建仓库"><a href="#创建仓库" class="headerlink" title="创建仓库"></a>创建仓库</h4><p>点击顶部配置图标，切到配置页面，左侧菜单选Repositories, 打开仓库配置，点击Create repository 创建仓库。<br><img src="https://blog-img.pocketdigi.com/file/2018/06/15279104550726.jpg" alt>￼ 我之前已经创建好，配置如下图<br><img src="https://blog-img.pocketdigi.com/file/2018/06/15279108350560.jpg" alt>￼ 注意，这里HTTP设置了一个10008端口，仅供docker使用，找个不冲突的就行。</p><h4 id="docker配置"><a href="#docker配置" class="headerlink" title="docker配置"></a>docker配置</h4><p>docker仓库默认需要https支持，但nexus没有配ssl,需要将服务器加到insecure-registries。<br><code>vim /etc/docker/daemon.json</code></p><pre><code>{  &quot;registry-mirrors&quot;: [&quot;https://registry.docker-cn.com&quot;],  &quot;insecure-registries&quot;:[&quot;192.168.0.201:10008&quot;]}</code></pre><p>我的nexus部署在192.168.0.201 后面的端口10008与创建仓库时对应。 登录仓库:<br><code>docker login 192.169.0.201:10008</code><br>按提示输入nexus的账号密码。登录一次以后，会自动保存，以后pull/push都不需要再登录。</p><h4 id="docker-提交镜像"><a href="#docker-提交镜像" class="headerlink" title="docker 提交镜像"></a>docker 提交镜像</h4><ol><li><p>将正在运行的container打包成image<br><img src="https://blog-img.pocketdigi.com/file/2018/06/15279118931001.jpg" alt>￼<br>如图，nginx2容器基于nginx镜像，但是改了一些配置， 现在我们将其打包成image<br><code>docker commit -m &quot;nginx modified&quot; -a &quot;Exception&quot; cb4cbbcde646 nginx-modified</code> <code>-m</code> 描述<br><code>-a</code> 作者<br><code>cb4cbbcde646</code> container id<br><code>nginx-modified</code> 生成的image名<br>现在再看镜像列表:<br><img src="https://blog-img.pocketdigi.com/file/2018/06/15279167648438.jpg" alt>￼￼<br>发现我们刚刚commit的image已经在里面了。</p></li><li><p>给image打标签<br><code>docker tag f3b408f36cf7 192.168.0.201:10008/nginx-modified</code> <code>f3b408f36cf7</code> image id<br><code>192.168.0.201:10008/nginx-modified</code> 仓库地址和保存路径</p></li><li><p>推送镜像<br><code>docker push 192.168.0.201:10008/nginx-modified</code></p></li><li><p>下载镜像<br>在另一台服务器上，如果要下载镜像,重复上面的<strong>docker配置</strong>步骤,然后pull<br><code>docker pull 192.168.0.201:10008/nginx-modified</code></p><p><img src="https://blog-img.pocketdigi.com/file/2018/06/15279190756281.jpg" alt>￼</p></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h4 id=&quot;为什么要建docker私服&quot;&gt;&lt;a href=&quot;#为什么要建docker私服&quot; class=&quot;headerlink&quot; title=&quot;为什么要建docker私服&quot;&gt;&lt;/a&gt;为什么要建docker私服&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;官服在国外，速度慢&lt;/li&gt;
&lt;li&gt;创
      
    
    </summary>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Linux/"/>
    
    
      <category term="blog" scheme="http://www.pocketdigi.com/tags/blog/"/>
    
  </entry>
  
  <entry>
    <title>systemctl开机启动zookeeper</title>
    <link href="http://www.pocketdigi.com/20180131/1593.html"/>
    <id>http://www.pocketdigi.com/20180131/1593.html</id>
    <published>2018-01-31T07:28:54.000Z</published>
    <updated>2019-08-20T08:11:05.220Z</updated>
    
    <content type="html"><![CDATA[<p>用systemctl使zookeeper开机启动，网上看了好多章，没一个好使，自己琢磨折腾了一个下午总算把它搞定。<br>直接上脚本：/etc/systemd/system/zookeeper.service</p><pre><code>[Unit]Description=zookeeper.serviceAfter=network.target[Service]Type=forkingEnvironment=ZOO_LOG_DIR=/usr/local/zookeeper-3.4.11/Environment=PATH=/usr/local/jdk1.8.0_152/bin:/usr/local/jdk1.8.0_152/jre/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/binExecStart=/usr/local/zookeeper-3.4.11/bin/zkServer.sh startExecStop=/usr/local/zookeeper-3.4.11/bin/zkServer.sh stopExecReload=/usr/local/zookeeper-3.4.11/bin/zkServer.sh restartPIDFile=/tmp/zookeeper/zookeeper_server.pidUser=www[Install]WantedBy=multi-user.target</code></pre><p>重点在两行Environment。</p><ul><li>第一行设置日志目录，如果没有设置，默认是当前目录，对www用户来说，可能没有权限。</li><li>第二行是配置环境变量，systemd用户实例不会继承类似.bashrc中定义的环境变量，所以是找不到jdk目录的，而zookeeper又必须有。</li></ul><p>保存后，reload</p><pre><code>systemctl daemon-reload</code></pre><p>启用开机自启</p><pre><code>systemctl enable zookeeper</code></pre><p>启动服务</p><pre><code>systemctl start zookeeper</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;用systemctl使zookeeper开机启动，网上看了好多章，没一个好使，自己琢磨折腾了一个下午总算把它搞定。&lt;br&gt;直接上脚本：/etc/systemd/system/zookeeper.service&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Descriptio
      
    
    </summary>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Linux/"/>
    
    
      <category term="blog" scheme="http://www.pocketdigi.com/tags/blog/"/>
    
  </entry>
  
  <entry>
    <title>Springboot 启动脚本</title>
    <link href="http://www.pocketdigi.com/20180127/1592.html"/>
    <id>http://www.pocketdigi.com/20180127/1592.html</id>
    <published>2018-01-27T09:20:00.000Z</published>
    <updated>2019-08-20T08:11:05.219Z</updated>
    
    <content type="html"><![CDATA[<p>Springboot项目可以直接通过</p><pre><code>java -jar xxx.jar</code></pre><p>命令启动,但是开启关闭还是有个脚本会比较方便。自己写了个shell脚本，功能是查找当前目录下的jar文件，然后启动，也可以关闭，重启。经过几次修改，个人觉得比较完美了，放出源码:</p><pre><code>#! /bin/bash# springboot的jar放同级目录下即可，只能有一个jar文件export PATH=$JAVA_HOME/bin:$PATHCURRENT_PATH=$(cd &quot;$(dirname &quot;$0&quot;)&quot;; pwd)JAR=$(find $CURRENT_PATH -maxdepth 1 -name &quot;*.jar&quot;)PID=$(ps -ef | grep $JAR | grep -v grep | awk &apos;{ print $2 }&apos;)case &quot;$1&quot; in    start)        if [ ! -z &quot;$PID&quot; ]; then            echo &quot;$JAR 已经启动，进程号: $PID&quot;        else            echo -n -e &quot;启动 $JAR ... \n&quot;            cd $CURRENT_PATH        nohup java -jar $JAR &gt;/dev/null 2&gt;&amp;1 &amp;            if [ &quot;$?&quot;=&quot;0&quot; ]; then                echo &quot;启动完成，请查看日志确保成功&quot;            else                echo &quot;启动失败&quot;            fi        fi        ;;    stop)        if [ -z &quot;$PID&quot; ]; then            echo &quot;$JAR 没有在运行，无需关闭&quot;        else            echo &quot;关闭 $JAR ...&quot;              kill -9 $PID            if [ &quot;$?&quot;=&quot;0&quot; ]; then                echo &quot;服务已关闭&quot;            else                echo &quot;服务关闭失败&quot;            fi        fi        ;;    restart)        ${0} stop        ${0} start        ;;    kill)        echo &quot;强制关闭 $JAR&quot;        killall $JAR        if [ &quot;$?&quot;=&quot;0&quot; ]; then            echo &quot;成功&quot;        else            echo &quot;失败&quot;        fi        ;;    status)        if [ ! -z &quot;$PID&quot; ]; then            echo &quot;$JAR 正在运行&quot;        else            echo &quot;$JAR 未在运行&quot;        fi        ;;  *)    echo &quot;Usage: ./springboot {start|stop|restart|status|kill}&quot; &gt;&amp;2        exit 1esac</code></pre><p>脚本会找到当前目录下的jar文件，所以，只能放一个jar文件。 开机启动脚本:</p><pre><code>[Unit]Description=Dubbo-adminWants=network.target[Service]Environment=JAVA_HOME=/usr/local/jdk1.8.0_171Type=forkingExecStart=/usr/local/xxx/springboot startExecStop=/usr/local/xxx/springboot stop[Install]WantedBy=multi-user.target</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Springboot项目可以直接通过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -jar xxx.jar&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;命令启动,但是开启关闭还是有个脚本会比较方便。自己写了个shell脚本，功能是查找当前目录下的jar文件，然后启动，也可以关闭，重启。经过几
      
    
    </summary>
    
      <category term="Linux" scheme="http://www.pocketdigi.com/categories/Linux/"/>
    
    
      <category term="blog" scheme="http://www.pocketdigi.com/tags/blog/"/>
    
  </entry>
  
  <entry>
    <title>使用Logstash同步mysql到Elasticsearch 6.0.1</title>
    <link href="http://www.pocketdigi.com/20171212/1585.html"/>
    <id>http://www.pocketdigi.com/20171212/1585.html</id>
    <published>2017-12-11T18:05:40.000Z</published>
    <updated>2019-08-20T08:11:05.219Z</updated>
    
    <content type="html"><![CDATA[<p>Logstash是一个数据收集管道，配置输入输出，可将数据从一个地方传到另一个地方。同步mysql到Elasticsearch，这里的输入，指的是mysql,输出就是Elasticsearch。 新版本的Logstash和Elasticsearch跟之前老版本的有些不同，所以我也是自己折腾了小半天，总算成功。</p><blockquote><p>下面的操作，Logstash和Elasticsearch的版本都是6.0.1</p></blockquote><hr><ol><li><p>下载安装 Logstash和Elasticsearch就不介绍了，下载后解压即可使用。 默认Logstash不包含读取数据库的jdbc插件，需要手动下载。进入Logstash的bin目录，执行：</p><pre><code>./logstash-plugin install logstash-input-jdbc</code></pre></li></ol><pre><code>因为网络原因，安装可能费点时间。</code></pre><ol start="2"><li><p>在某处建一个目录，放置配置文件(哪不重要，因为执行时会配置路径)，我这放到bin下的mysql目录里。</p></li><li><p>复制mysql jdbc驱动(mysql-connector-java-5.1.35.jar)到该目录下</p></li><li><p>编写导出数据的sql，放到sql.sql(文件名自己取)里,内容类似这样：</p><pre><code>select *, id as car_id from violation_car car where car.gmt_modified&gt;= :sql_last_value</code></pre></li></ol><pre><code>这里需要介绍下，我的表里有个gmt\_modified字段，用于记录该条记录的最后修改时间，sql\_last_value是Logstash查询后，保存的上次查询时间，第一次查询时，该值是1970/1/1,所以第一次导入，如果你的表现有数据很多，可能会有点问题，后面会根据最后修改时间，更新修改过的数据。</code></pre><ol start="5"><li><p>编写输入输出配置文件jdbc.conf</p><pre><code>input {  jdbc {    jdbc_driver_library =&gt; &quot;/xxx/xxx/logstash-6.0.1/bin/mysql/mysql-connector-java-5.1.35.jar&quot;    jdbc_driver_class =&gt; &quot;com.mysql.jdbc.Driver&quot;    jdbc_connection_string =&gt; &quot;jdbc:mysql://localhost:3306/violation&quot;    jdbc_user =&gt; &quot;root&quot;    jdbc_password =&gt; &quot;&quot;    schedule =&gt; &quot;* * * * *&quot;    jdbc_default_timezone =&gt; &quot;Asia/Shanghai&quot;    statement_filepath =&gt; &quot;/xxx/xxx/logstash-6.0.1/bin/mysql/sql.sql&quot;    use_column_value  =&gt; false    last_run_metadata_path =&gt; &quot;/xxx/xxx/logstash-6.0.1/bin/mysql/last_run.txt&quot;  }}output {    elasticsearch {        hosts =&gt; [&quot;127.0.0.1:9200&quot;]        index =&gt; &quot;violation&quot;        document_id =&gt; &quot;%{car_id}&quot;        document_type =&gt; &quot;car&quot;    }    stdout {        codec =&gt; json_lines    }}</code></pre></li></ol><pre><code>字段介绍：    input.jdbc.jdbc_driver_library  jdbc驱动的位置    input.jdbc.jdbc_driver_class    驱动类名    input.jdbc.jdbc_connection_string   数据库连接字符串    input.jdbc.jdbc_user    用户名    input.jdbc.jdbc_password  密码    input.jdbc.schedule   更新计划(参考linux crontab)    input.jdbc.jdbc_default_timezone   时区，默认没有时区，日志里时间差8小时，中国需要用Asia/Shanghai    input.jdbc.statement_filepath 导出数据的sql文件，就是上面写的    input.jdbc.use_column_value  如果是true,sql_last_value是tracking_column指定字段的数字值，false就是时间，默认是false    input.jdbc.last_run_metadata_path  保存sql_last_value值文件的位置    output.elasticsearch.hosts elasticsearch服务器，填多个，请求会负载均衡。    output.elasticsearch.index 索引名    output.elasticsearch.document_id   生成文件的id,这里使用sql产生的car_id    output.elasticsearch.document_type  文档类型     output.stdout 配置的是命令行输出，使用json</code></pre><ol start="6"><li><p>配置完以后，启动</p><pre><code>./logstash -f mysql/jdbc.conf</code></pre></li></ol><pre><code>按上面的配置，logstash会每分钟查询一次表，看是否会更新，有更新则提交到Elasticsearch</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Logstash是一个数据收集管道，配置输入输出，可将数据从一个地方传到另一个地方。同步mysql到Elasticsearch，这里的输入，指的是mysql,输出就是Elasticsearch。 新版本的Logstash和Elasticsearch跟之前老版本的有些不同，所
      
    
    </summary>
    
      <category term="Java" scheme="http://www.pocketdigi.com/categories/Java/"/>
    
    
      <category term="mysql" scheme="http://www.pocketdigi.com/tags/mysql/"/>
    
      <category term="elasticsearch" scheme="http://www.pocketdigi.com/tags/elasticsearch/"/>
    
      <category term="logstash" scheme="http://www.pocketdigi.com/tags/logstash/"/>
    
  </entry>
  
  <entry>
    <title>Java读取RSA pem公钥 使用公钥验证签名</title>
    <link href="http://www.pocketdigi.com/20170927/1582.html"/>
    <id>http://www.pocketdigi.com/20170927/1582.html</id>
    <published>2017-09-27T09:18:31.000Z</published>
    <updated>2019-08-20T08:11:04.877Z</updated>
    
    <content type="html"><![CDATA[<p>之所以要记录是因为网上找了好久，试了好多方法，基本上都不好使. 签名sign做了base64</p><pre><code>    BASE64Decoder base64decoder = new BASE64Decoder();try {    //读取pem证书    BufferedReader br = new BufferedReader(new FileReader(&quot;xxx.pem&quot;));    String s = br.readLine();    StringBuffer publickey = new StringBuffer();    while (s.charAt(0) != &apos;-&apos;) {        publickey.append(s + &quot;\r&quot;);        s = br.readLine();    }    byte[] keybyte = base64decoder.decodeBuffer(publickey.toString());    KeyFactory kf = KeyFactory.getInstance(&quot;RSA&quot;);    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keybyte);    PublicKey publicKey = kf.generatePublic(keySpec);    BASE64Encoder bse=new BASE64Encoder();    System.out.println(&quot;pk:&quot;+bse.encode(publicKey.getEncoded()));    //被签的原文    String toSign=&quot;sxxxsasdsss&quot;;    //生成的签名    String sign=&quot;xxxxx&quot;;    Signature signature = Signature.getInstance(&quot;SHA1withRSA&quot;);    signature.initVerify(publicKey);    signature.update(toSign.getBytes());    boolean verify = signature.verify(base64decoder        .decodeBuffer(            sign));    System.out.println(verify);} catch (Exception e) {    e.printStackTrace();}</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;之所以要记录是因为网上找了好久，试了好多方法，基本上都不好使. 签名sign做了base64&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    BASE64Decoder base64decoder = new BASE64Decoder();
try {
    //读取pem证书

      
    
    </summary>
    
      <category term="Java" scheme="http://www.pocketdigi.com/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>Springboot 禁用数据库自动配置</title>
    <link href="http://www.pocketdigi.com/20170826/1580.html"/>
    <id>http://www.pocketdigi.com/20170826/1580.html</id>
    <published>2017-08-26T08:22:57.000Z</published>
    <updated>2019-08-20T08:11:02.571Z</updated>
    
    <content type="html"><![CDATA[<p>如果我们用SpringBoot实现一个简单的微服务，不需要数据库，你会发现在写完代码启动时会报 <code>org.springframework.beans.factory.BeanCreationException: Error creating bean with name &#39;spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties&#39;</code> 之类的错误。这是因为SpringBoot默认会自动配置数据库，如果业务不需要，就要手动禁用数据库自动配置,在Application的SpringBootApplication注解里加上 <code>@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})</code></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;如果我们用SpringBoot实现一个简单的微服务，不需要数据库，你会发现在写完代码启动时会报 &lt;code&gt;org.springframework.beans.factory.BeanCreationException: Error creating bean with n
      
    
    </summary>
    
      <category term="Java" scheme="http://www.pocketdigi.com/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>Spring boot+mybatis+druid+SQL监控配置</title>
    <link href="http://www.pocketdigi.com/20170530/1577.html"/>
    <id>http://www.pocketdigi.com/20170530/1577.html</id>
    <published>2017-05-30T03:06:00.000Z</published>
    <updated>2019-08-20T08:11:06.522Z</updated>
    
    <content type="html"><![CDATA[<p>之前写过一篇<a href="https://www.pocketdigi.com/20170409/1570.html">搭建Spring boot + mybatis + freemarker项目</a>,Spring boot默认的数据库连接池是tomcat-jdbc，今天我们要用druid替换它。</p><h3 id="什么是druid"><a href="#什么是druid" class="headerlink" title="什么是druid"></a>什么是druid</h3><p>druid是阿里巴巴开源的数据库连接池，自称是Java语言中最好的数据库连接池，提供强大的监控和扩展功能。</p><h3 id="为什么用druid"><a href="#为什么用druid" class="headerlink" title="为什么用druid"></a>为什么用druid</h3><ol><li><p>性能</p><p>官方数据<a href="https://github.com/alibaba/druid/wiki/Benchmark_aliyun" target="_blank" rel="noopener">Benchmark_aliyun</a>,druid在响应时间上优于其他几个线程池。非官方的测试数据可能差距没这么明显，但仍然高于其他几个线程池。</p></li><li><p>自带监控功能</p><p>自带监控，可帮助开发者找出慢查询，查看并发数等。</p></li></ol><h3 id="配置步骤："><a href="#配置步骤：" class="headerlink" title="配置步骤："></a>配置步骤：</h3><ol><li><p>基于干净的spring boot web项目，添加mysql、mybatis、druid库:</p><pre><code>&lt;dependency&gt;    &lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;    &lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;    &lt;version&gt;1.3.0&lt;/version&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;mysql&lt;/groupId&gt;    &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;    &lt;scope&gt;runtime&lt;/scope&gt;&lt;/dependency&gt;&lt;dependency&gt;    &lt;groupId&gt;com.alibaba&lt;/groupId&gt;    &lt;artifactId&gt;druid&lt;/artifactId&gt;    &lt;version&gt;1.0.31&lt;/version&gt;&lt;/dependency&gt;</code></pre></li></ol><ol start="2"><li><p>配置数据库连接</p><p>application.properties:</p><pre><code>spring.datasource.driverClassName=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&amp;characterEncoding=utf8spring.datasource.username=rootspring.datasource.password=</code></pre></li></ol><pre><code>这里key叫什么其实并不重要，因为后面配置数据源，我们会自己读取配置。</code></pre><ol start="3"><li><p>Mybatis、druid配置<br>新建<code>MyBatisConfig.java</code>:</p><pre><code>package com.pocketdigi.config.database;import com.alibaba.druid.pool.DruidDataSourceFactory;import org.mybatis.spring.SqlSessionFactoryBean;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.core.io.support.PathMatchingResourcePatternResolver;import org.springframework.core.io.support.ResourcePatternResolver;import javax.sql.DataSource;import java.io.IOException;import java.util.HashMap;import java.util.Map;@Configuration@MapperScan(&quot;com.pocketdigi.dal.mapper&quot;)public class MyBatisConfig {    @Value(&quot;${spring.datasource.url}&quot;)    String dbUrl;    @Value(&quot;${spring.datasource.username}&quot;)    String userName;    @Value(&quot;${spring.datasource.password}&quot;)    String password;    @Value(&quot;${spring.datasource.driverClassName}&quot;)    String driverClassName;    private static String MYBATIS_CONFIG = &quot;mybatis_config.xml&quot;;    private static String MAPPER_PATH = &quot;/mapper/*.xml&quot;;    @Bean    public SqlSessionFactoryBean createSqlSessionFactoryBean() throws IOException {        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();        //设置mybatis configuration 扫描路径        sqlSessionFactoryBean.setConfigLocation(new ClassPathResource(MYBATIS_CONFIG));        //添加mapper 扫描路径        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + MAPPER_PATH;        sqlSessionFactoryBean.setMapperLocations(pathMatchingResourcePatternResolver.getResources(packageSearchPath));        //设置datasource        sqlSessionFactoryBean.setDataSource(dataSource());        return sqlSessionFactoryBean;    }    private DataSource dataSource() {        Map&lt;String,Object&gt; properties=new HashMap&lt;&gt;();        properties.put(DruidDataSourceFactory.PROP_DRIVERCLASSNAME,driverClassName);        properties.put(DruidDataSourceFactory.PROP_URL,dbUrl);        properties.put(DruidDataSourceFactory.PROP_USERNAME,userName);        properties.put(DruidDataSourceFactory.PROP_PASSWORD,password);        //添加统计、SQL注入、日志过滤器        properties.put(DruidDataSourceFactory.PROP_FILTERS,&quot;stat,wall,log4j2&quot;);        //sql合并，慢查询定义为5s        properties.put(DruidDataSourceFactory.PROP_CONNECTIONPROPERTIES,&quot;druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000&quot;);        try {            return DruidDataSourceFactory.createDataSource(properties);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }}</code></pre></li></ol><pre><code>mybatis的mapper接口在com.pocketdigi.dal.mapper，mybatis_config.xml在resources目录下，xml mapper文件在resources/mapper/目录下。mybatis_config.xml:    &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;    &lt;!DOCTYPE configuration PUBLIC &quot;-//mybatis.org//DTD Config 3.0//EN&quot; &quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;&gt;    &lt;configuration&gt;        &lt;settings&gt;            &lt;!-- 所有映射器中配置的缓存的全局开关--&gt;            &lt;setting name=&quot;cacheEnabled&quot; value=&quot;false&quot;/&gt;            &lt;!-- 延迟加载的全局开关 配置二级缓存时将此属性关闭--&gt;            &lt;setting name=&quot;lazyLoadingEnabled&quot; value=&quot;false&quot;/&gt;            &lt;!-- 关联对象加载 配置二级缓存时将此属性关闭--&gt;            &lt;setting name=&quot;aggressiveLazyLoading&quot; value=&quot;false&quot;/&gt;            &lt;!-- 是否允许单一语句返回多结果集--&gt;            &lt;setting name=&quot;multipleResultSetsEnabled&quot; value=&quot;true&quot;/&gt;            &lt;!-- 使用列标签代替列名--&gt;            &lt;setting name=&quot;useColumnLabel&quot; value=&quot;true&quot;/&gt;            &lt;!-- 允许 JDBC 支持自动生成主键，需要驱动兼容 --&gt;            &lt;setting name=&quot;useGeneratedKeys&quot; value=&quot;false&quot;/&gt;            &lt;!-- 指定 MyBatis 是否以及如何自动映射指定的列到字段或属性--&gt;            &lt;setting name=&quot;autoMappingBehavior&quot; value=&quot;PARTIAL&quot;/&gt;            &lt;!-- 配置默认的执行器--&gt;            &lt;setting name=&quot;defaultExecutorType&quot; value=&quot;SIMPLE&quot;/&gt;            &lt;!-- 设置超时时间，它决定驱动等待数据库响应的秒数--&gt;            &lt;setting name=&quot;defaultStatementTimeout&quot; value=&quot;30&quot;/&gt;            &lt;!-- 允许在嵌套语句中使用行分界 --&gt;            &lt;setting name=&quot;safeRowBoundsEnabled&quot; value=&quot;false&quot;/&gt;            &lt;!-- 是否开启自动驼峰命名规则映射 --&gt;            &lt;setting name=&quot;mapUnderscoreToCamelCase&quot; value=&quot;false&quot;/&gt;            &lt;!-- 利用本地缓存机制防止循环引用和加速重复嵌套查询 默认值为 SESSION，这种情况下会缓存一个会话中执行的所有查询--&gt;            &lt;setting name=&quot;localCacheScope&quot; value=&quot;SESSION&quot;/&gt;            &lt;!-- 当没有为参数提供特定的 JDBC 类型时，为空值指定 JDBC 类型 --&gt;            &lt;setting name=&quot;jdbcTypeForNull&quot; value=&quot;OTHER&quot;/&gt;            &lt;!-- 指定哪些对象的方法触发一次延迟加载--&gt;            &lt;setting name=&quot;lazyLoadTriggerMethods&quot; value=&quot;equals,clone,hashCode,toString&quot;/&gt;        &lt;/settings&gt;        &lt;typeAliases&gt;            &lt;package name=&quot;com.pocketdigi.dal.po&quot;/&gt;        &lt;/typeAliases&gt;    &lt;/configuration&gt;数据库对应的POJO类在com.pocketdigi.dal.po</code></pre><ol start="4"><li><p>druid监控后台配置</p><p>druid监控后台需要配置一个Filter和一个Servlet,将指定的路径转发到com.alibaba.druid.support.http.StatViewServlet</p><p>ServletConfiguration.java:</p><pre><code>@Configurationpublic class ServletConfiguration {    @Bean    public ServletRegistrationBean druidStatViewServletBean() {        //后台的路径        ServletRegistrationBean statViewServletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), &quot;/druid/*&quot;);        Map&lt;String,String&gt; params = new HashMap&lt;&gt;();        //账号密码，是否允许重置数据        params.put(&quot;loginUsername&quot;,&quot;admin&quot;);        params.put(&quot;loginPassword&quot;,&quot;admin&quot;);        params.put(&quot;resetEnable&quot;,&quot;true&quot;);        statViewServletRegistrationBean.setInitParameters(params);        return statViewServletRegistrationBean;    }}</code></pre></li></ol><pre><code>FilterConfiguration.java    @Configuration    public class FilterConfiguration {        @Bean        public FilterRegistrationBean druidStatFilterBean() {            FilterRegistrationBean druidStatFilterBean=new FilterRegistrationBean(new WebStatFilter());            List&lt;String&gt; urlPattern=new ArrayList&lt;&gt;();            urlPattern.add(&quot;/*&quot;);            druidStatFilterBean.setUrlPatterns(urlPattern);            Map&lt;String,String&gt; initParams=new HashMap&lt;&gt;();            initParams.put(&quot;exclusions&quot;,&quot;*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*&quot;);            druidStatFilterBean.setInitParameters(initParams);            return druidStatFilterBean;        }    }配置完成，启动应用，打开[http://localhost:8000/druid/](http://localhost:8000/druid/) 用ServletConfiguration.java配置的账号密码登录，即可进入druid监控后台。</code></pre><p>代码已传github <a href="https://github.com/pocketdigi/springboot-demo" target="_blank" rel="noopener">https://github.com/pocketdigi/springboot-demo</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;之前写过一篇&lt;a href=&quot;https://www.pocketdigi.com/20170409/1570.html&quot;&gt;搭建Spring boot + mybatis + freemarker项目&lt;/a&gt;,Spring boot默认的数据库连接池是tomcat-jdbc
      
    
    </summary>
    
      <category term="Java" scheme="http://www.pocketdigi.com/categories/Java/"/>
    
    
      <category term="blog" scheme="http://www.pocketdigi.com/tags/blog/"/>
    
      <category term="druid" scheme="http://www.pocketdigi.com/tags/druid/"/>
    
      <category term="mybatis" scheme="http://www.pocketdigi.com/tags/mybatis/"/>
    
      <category term="springboot" scheme="http://www.pocketdigi.com/tags/springboot/"/>
    
      <category term="监控" scheme="http://www.pocketdigi.com/tags/%E7%9B%91%E6%8E%A7/"/>
    
  </entry>
  
  <entry>
    <title>Spring boot 注册Servlet和Filter</title>
    <link href="http://www.pocketdigi.com/20170527/1576.html"/>
    <id>http://www.pocketdigi.com/20170527/1576.html</id>
    <published>2017-05-27T06:51:31.000Z</published>
    <updated>2019-08-20T08:11:06.530Z</updated>
    
    <content type="html"><![CDATA[<p>使用Spring开发web程序，在大部分情况下，都是通过Spring默认的DispatcherServlet,转发请求到Controller,我们在Controller里处理请求。但有时候，可能有些请求我们不希望通过Spring,而是通过其他Servlet处理。如果是普通的Spring项目，注册Filter和Servlet只需在web.xml时添加filter、filter-mapping、servlet、servlet-mapping，但Spring boot项目里没有web.xml。 Spring boot有两种方法注册Servlet和Filter:代码注册、注解注册。 先写个简单的Servlet:</p><pre><code>@Slf4jpublic class TestServlet extends HttpServlet{    @Override    public void init(ServletConfig config) throws ServletException {        super.init(config);        String value=config.getInitParameter(&quot;param1&quot;);        log.info(&quot;param1:{}&quot;,value);    }    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        PrintWriter out = resp.getWriter();        out.println(&quot;&lt;html&gt;&quot;);        out.println(&quot;&lt;head&gt;&quot;);        out.println(&quot;&lt;title&gt;Hello World&lt;/title&gt;&quot;);        out.println(&quot;&lt;/head&gt;&quot;);        out.println(&quot;&lt;body&gt;&quot;);        out.println(&quot;Hello&quot;);        out.println(&quot;&lt;/body&gt;&quot;);        out.println(&quot;&lt;/html&gt;&quot;);    }}</code></pre><h3 id="代码注册"><a href="#代码注册" class="headerlink" title="代码注册:"></a>代码注册:</h3><p>代码注册通过ServletRegistrationBean和FilterRegistrationBean两个类，注册Servlet和Filter。 在Application类里添加注册代码：</p><pre><code>@SpringBootApplicationpublic class DemoApplication {    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);    }    @Bean    public ServletRegistrationBean testServletBean() {        ServletRegistrationBean testServletRegistration = new ServletRegistrationBean(new TestServlet(), &quot;/test1/&quot;);        Map&lt;String,String&gt; params = new HashMap&lt;&gt;();        params.put(&quot;param1&quot;,&quot;value2&quot;);        testServletRegistration.setInitParameters(params);        return testServletRegistration;    }}</code></pre><p>三个参数，servlet实例、url mapping、init-param, Filter也类似，只是用FilterRegistrationBean。 或者，放到一个单独的配置类里：</p><pre><code>@Configurationpublic class ServletConfiguration {    @Bean    public ServletRegistrationBean testServletBean() {        ServletRegistrationBean testServletRegistration = new ServletRegistrationBean(new TestServlet(), &quot;/test1/&quot;);        Map&lt;String,String&gt; params = new HashMap&lt;&gt;();        params.put(&quot;param1&quot;,&quot;value2&quot;);        testServletRegistration.setInitParameters(params);        return testServletRegistration;    }}</code></pre><p>建议拆开放到单独的类里，如果所有配置都放Application里，乱。</p><h3 id="注解注册"><a href="#注解注册" class="headerlink" title="注解注册"></a>注解注册</h3><p>注解注册需要在Servlet类上加上@WebServlet,@WebFilter注解，Application里加上@ServletComponentScan开启Servlet注解扫描。 修改TestServlet</p><pre><code>@Slf4j@WebServlet(urlPatterns = &quot;/test/*&quot;,initParams = {        @WebInitParam(name = &quot;param1&quot;, value = &quot;value1&quot;),})public class TestServlet extends HttpServlet{    @Override    public void init(ServletConfig config) throws ServletException {        super.init(config);        String value=config.getInitParameter(&quot;param1&quot;);        log.info(&quot;param1:{}&quot;,value);    }    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        PrintWriter out = resp.getWriter();        out.println(&quot;&lt;html&gt;&quot;);        out.println(&quot;&lt;head&gt;&quot;);        out.println(&quot;&lt;title&gt;Hello World&lt;/title&gt;&quot;);        out.println(&quot;&lt;/head&gt;&quot;);        out.println(&quot;&lt;body&gt;&quot;);        out.println(&quot;Hello&quot;);        out.println(&quot;&lt;/body&gt;&quot;);        out.println(&quot;&lt;/html&gt;&quot;);    }}</code></pre><p>如果是Filter:</p><pre><code>@WebFilter(filterName = &quot;webFilter&quot;, urlPatterns = &quot;/*&quot;,        initParams = {@WebInitParam(name = &quot;exclusions&quot;, value = &quot;*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico&quot;)})public class WebFilter extends Filter {}</code></pre><p>Application:</p><pre><code>@SpringBootApplication@ServletComponentScanpublic class DemoApplication {    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);    }   }</code></pre><h3 id="怎么选择？"><a href="#怎么选择？" class="headerlink" title="怎么选择？"></a>怎么选择？</h3><p>如果是自己实现的Servlet,那么直接加个注解，即可注册。但Servlet不是自己实现的话，用代码注册比较好，否则需要继承原来的Servlet加一个子类。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;使用Spring开发web程序，在大部分情况下，都是通过Spring默认的DispatcherServlet,转发请求到Controller,我们在Controller里处理请求。但有时候，可能有些请求我们不希望通过Spring,而是通过其他Servlet处理。如果是普通的
      
    
    </summary>
    
      <category term="Java" scheme="http://www.pocketdigi.com/categories/Java/"/>
    
    
      <category term="blog" scheme="http://www.pocketdigi.com/tags/blog/"/>
    
      <category term="Filter" scheme="http://www.pocketdigi.com/tags/Filter/"/>
    
      <category term="Servlet" scheme="http://www.pocketdigi.com/tags/Servlet/"/>
    
      <category term="spring boot" scheme="http://www.pocketdigi.com/tags/spring-boot/"/>
    
  </entry>
  
  <entry>
    <title>Java jstack用法</title>
    <link href="http://www.pocketdigi.com/20170523/1574.html"/>
    <id>http://www.pocketdigi.com/20170523/1574.html</id>
    <published>2017-05-23T04:39:02.000Z</published>
    <updated>2019-08-20T08:11:06.527Z</updated>
    
    <content type="html"><![CDATA[<p>jstack是jdk中自带的用于查看进程内线程栈的工具。当程序出现死锁时，我们可以通过jstack打印线程栈找到问题。</p><h3 id="找出代码中的死锁"><a href="#找出代码中的死锁" class="headerlink" title="找出代码中的死锁"></a>找出代码中的死锁</h3><p>学习从一段简单的代码开始:</p><pre><code>public class Main {    public static void main(String[] args) {        test1();    }    private static void test1() {        final Object lock1 = new Object();        final Object lock2 = new Object();        Thread thread=new Thread(new Runnable() {            public void run() {                synchronized (lock1) {                    try {                        for(int i=0;i&lt;20;i++) {                            Thread.sleep(1000);                            System.out.println(Thread.currentThread().getName()+&quot; &quot;+i);                        }                        synchronized (lock2) {                            for(int i=20;i&lt;40;i++) {                                Thread.sleep(1000);                                System.out.println(Thread.currentThread().getName()+&quot; &quot;+i);                            }                        }                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        thread.start();        synchronized (lock2) {            try {                for(int i=0;i&lt;20;i++) {                    Thread.sleep(1000);                    System.out.println(Thread.currentThread().getName()+&quot; &quot;+i);                }                synchronized (lock1) {                    for(int i=20;i&lt;40;i++) {                        Thread.sleep(1000);                        System.out.println(Thread.currentThread().getName()+&quot; &quot;+i);                    }                }            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}</code></pre><p>肉眼都能看出来的死锁，程序运行20s后进入死锁状态。<br>首先我们先找出这个程序的进程id,建议用jps,而不是ps,原因是jps只会显示当前用户下面的java进程，等于自动帮我们过滤了。</p><pre><code>jps -l</code></pre><p><img src="https://img.pocketdigi.com/blog/1495518894.jpg" alt></p><p>Main进程，id 5632,这时出动我们的主角jstack.</p><pre><code>jstack 5632</code></pre><p>结果如下</p><pre><code>2017-05-23 13:57:22Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.45-b02 mixed mode):&quot;Attach Listener&quot; #12 daemon prio=9 os_prio=31 tid=0x00007ff0f780c800 nid=0x4507 waiting on condition [0x0000000000000000]   java.lang.Thread.State: RUNNABLE&quot;Thread-0&quot; #11 prio=5 os_prio=31 tid=0x00007ff0f80c0000 nid=0x5a03 waiting for monitor entry [0x0000700002310000]   java.lang.Thread.State: BLOCKED (on object monitor)    at Main$1.run(Main.java:33)    - waiting to lock &lt;0x000000076adb1a98&gt; (a java.lang.Object)    - locked &lt;0x000000076adb1a88&gt; (a java.lang.Object)    at java.lang.Thread.run(Thread.java:745)&quot;Service Thread&quot; #10 daemon prio=9 os_prio=31 tid=0x00007ff0f980b800 nid=0x5603 runnable [0x0000000000000000]   java.lang.Thread.State: RUNNABLE&quot;C1 CompilerThread3&quot; #9 daemon prio=9 os_prio=31 tid=0x00007ff0f6803000 nid=0x5403 waiting on condition [0x0000000000000000]   java.lang.Thread.State: RUNNABLE&quot;C2 CompilerThread2&quot; #8 daemon prio=9 os_prio=31 tid=0x00007ff0f6802800 nid=0x5203 waiting on condition [0x0000000000000000]   java.lang.Thread.State: RUNNABLE&quot;C2 CompilerThread1&quot; #7 daemon prio=9 os_prio=31 tid=0x00007ff0f6801800 nid=0x5003 waiting on condition [0x0000000000000000]   java.lang.Thread.State: RUNNABLE&quot;C2 CompilerThread0&quot; #6 daemon prio=9 os_prio=31 tid=0x00007ff0f90de800 nid=0x4e03 waiting on condition [0x0000000000000000]   java.lang.Thread.State: RUNNABLE&quot;Monitor Ctrl-Break&quot; #5 daemon prio=5 os_prio=31 tid=0x00007ff0f90dc800 nid=0x4c03 runnable [0x0000700001bfb000]   java.lang.Thread.State: RUNNABLE    at java.net.SocketInputStream.socketRead0(Native Method)    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)    at java.net.SocketInputStream.read(SocketInputStream.java:170)    at java.net.SocketInputStream.read(SocketInputStream.java:141)    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)    - locked &lt;0x000000076af12b68&gt; (a java.io.InputStreamReader)    at java.io.InputStreamReader.read(InputStreamReader.java:184)    at java.io.BufferedReader.fill(BufferedReader.java:161)    at java.io.BufferedReader.readLine(BufferedReader.java:324)    - locked &lt;0x000000076af12b68&gt; (a java.io.InputStreamReader)    at java.io.BufferedReader.readLine(BufferedReader.java:389)    at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)&quot;Signal Dispatcher&quot; #4 daemon prio=9 os_prio=31 tid=0x00007ff0f8039800 nid=0x4a03 runnable [0x0000000000000000]   java.lang.Thread.State: RUNNABLE&quot;Finalizer&quot; #3 daemon prio=8 os_prio=31 tid=0x00007ff0f9803000 nid=0x3903 in Object.wait() [0x0000700001972000]   java.lang.Thread.State: WAITING (on object monitor)    at java.lang.Object.wait(Native Method)    - waiting on &lt;0x000000076ab06f58&gt; (a java.lang.ref.ReferenceQueue$Lock)    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)    - locked &lt;0x000000076ab06f58&gt; (a java.lang.ref.ReferenceQueue$Lock)    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)&quot;Reference Handler&quot; #2 daemon prio=10 os_prio=31 tid=0x00007ff0f9802000 nid=0x3703 in Object.wait() [0x000070000186f000]   java.lang.Thread.State: WAITING (on object monitor)    at java.lang.Object.wait(Native Method)    - waiting on &lt;0x000000076ab06998&gt; (a java.lang.ref.Reference$Lock)    at java.lang.Object.wait(Object.java:502)    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)    - locked &lt;0x000000076ab06998&gt; (a java.lang.ref.Reference$Lock)&quot;main&quot; #1 prio=5 os_prio=31 tid=0x00007ff0f8002000 nid=0x1c03 waiting for monitor entry [0x0000700000e51000]   java.lang.Thread.State: BLOCKED (on object monitor)    at Main.test1(Main.java:52)    - waiting to lock &lt;0x000000076adb1a88&gt; (a java.lang.Object)    - locked &lt;0x000000076adb1a98&gt; (a java.lang.Object)    at Main.main(Main.java:18)&quot;VM Thread&quot; os_prio=31 tid=0x00007ff0f801f800 nid=0x3503 runnable&quot;GC task thread#0 (ParallelGC)&quot; os_prio=31 tid=0x00007ff0f9003800 nid=0x2503 runnable&quot;GC task thread#1 (ParallelGC)&quot; os_prio=31 tid=0x00007ff0f9004000 nid=0x2703 runnable&quot;GC task thread#2 (ParallelGC)&quot; os_prio=31 tid=0x00007ff0f7801000 nid=0x2903 runnable&quot;GC task thread#3 (ParallelGC)&quot; os_prio=31 tid=0x00007ff0f600e800 nid=0x2b03 runnable&quot;GC task thread#4 (ParallelGC)&quot; os_prio=31 tid=0x00007ff0f600f000 nid=0x2d03 runnable&quot;GC task thread#5 (ParallelGC)&quot; os_prio=31 tid=0x00007ff0f6010000 nid=0x2f03 runnable&quot;GC task thread#6 (ParallelGC)&quot; os_prio=31 tid=0x00007ff0f6010800 nid=0x3103 runnable&quot;GC task thread#7 (ParallelGC)&quot; os_prio=31 tid=0x00007ff0f6011000 nid=0x3303 runnable&quot;VM Periodic Task Thread&quot; os_prio=31 tid=0x00007ff0f980c000 nid=0x5803 waiting on conditionJNI global references: 26Found one Java-level deadlock:=============================&quot;Thread-0&quot;:  waiting to lock monitor 0x00007ff0f8024f38 (object 0x000000076adb1a98, a java.lang.Object),  which is held by &quot;main&quot;&quot;main&quot;:  waiting to lock monitor 0x00007ff0f8023bf8 (object 0x000000076adb1a88, a java.lang.Object),  which is held by &quot;Thread-0&quot;Java stack information for the threads listed above:===================================================&quot;Thread-0&quot;:    at Main$1.run(Main.java:33)    - waiting to lock &lt;0x000000076adb1a98&gt; (a java.lang.Object)    - locked &lt;0x000000076adb1a88&gt; (a java.lang.Object)    at java.lang.Thread.run(Thread.java:745)&quot;main&quot;:    at Main.test1(Main.java:52)    - waiting to lock &lt;0x000000076adb1a88&gt; (a java.lang.Object)    - locked &lt;0x000000076adb1a98&gt; (a java.lang.Object)    at Main.main(Main.java:18)Found 1 deadlock.</code></pre><p>jstack直接帮我们找出来了deadlock。真实场景下，如果代码比较复杂，可能需要我们自己分析找出死锁。<br>如果我们自己分析，该怎么找出死锁呢？重点分析<code>java.lang.Thread.State: BLOCKED</code>的进程，找到<br><code>waiting to lock</code>，看看这个锁的持有者，是不是也被锁着，沿着这个链路找下去，是不是死锁就能找出来 了。比如上面的例子中,<code>main</code>线程持有<code>0x000000076adb1a98</code>锁，等待<code>0x000000076adb1a88</code>锁释放，而<code>Thread-0</code>持有<code>0x000000076adb1a88</code>锁，等待<code>0x000000076adb1a98</code>锁释放，两个线程互相等待，而进入死锁状态。</p><h3 id="找出CPU消耗多的代码"><a href="#找出CPU消耗多的代码" class="headerlink" title="找出CPU消耗多的代码"></a>找出CPU消耗多的代码</h3><p>如果程序cpu占用很高，我们需要找到问题优化，可以配合top命令，找出最耗cpu的进程，从而找到相应代码解决问题。</p><ol><li><p>先用jps找出程序pid，这里是23034</p></li><li><p>用top命令找出该进程最耗cpu的线程。下面的top是linux中的，mac里的不一样。</p><pre><code>top -Hp 23034</code></pre></li></ol><pre><code>结果如下图  ![](https://img.pocketdigi.com/blog/1495522309.jpg)23046线程占了93.8的cpu,就是它.</code></pre><ol start="3"><li><p>将23046转成16进制。因为top里的pid是10进制，而jstack里是16进制，叫nid。<br>可以用printf命令转换</p><pre><code>printf &quot;%x\n&quot; 23046</code></pre></li></ol><pre><code>得到`5a06`</code></pre><ol start="4"><li><p>jstack出场</p><pre><code>jstack 23034 | grep 5a06</code></pre></li></ol><pre><code>结果如下：  ![](https://img.pocketdigi.com/blog/1495522365.jpg)writeFileThread占用了最多的cpu资源。找到后，可以优化代码了.</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;jstack是jdk中自带的用于查看进程内线程栈的工具。当程序出现死锁时，我们可以通过jstack打印线程栈找到问题。&lt;/p&gt;
&lt;h3 id=&quot;找出代码中的死锁&quot;&gt;&lt;a href=&quot;#找出代码中的死锁&quot; class=&quot;headerlink&quot; title=&quot;找出代码中的死锁&quot;
      
    
    </summary>
    
      <category term="Java" scheme="http://www.pocketdigi.com/categories/Java/"/>
    
    
      <category term="Java" scheme="http://www.pocketdigi.com/tags/Java/"/>
    
      <category term="blog" scheme="http://www.pocketdigi.com/tags/blog/"/>
    
      <category term="jstack" scheme="http://www.pocketdigi.com/tags/jstack/"/>
    
  </entry>
  
</feed>
