junbaor

junbaor 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织 github.com/junbaor 编辑
编辑

广泛涉略,快速学习
QQ:1904661626

个人动态

junbaor 发布了文章 · 8月20日

使用 Minikube 体验 Kubernetes

前言

k8s 这坑迟早是要入的, 还是早点研究明白比较好。生产中一般都会使用云平台的容器服务,所以暂时不关注二进制的安装, 先在本地搭一个开发环境,便于熟悉各个组件。

本地体验 kubernetes 一般会用 minikube 和 kind
https://github.com/kubernetes/minikube
https://github.com/kubernetes-sigs/kind

比较一下很容易做出选择, 毕竟 minikube 代码是放在 kubernetes 组织下, 并且 start 数明显高出一大截。

前提条件

  • MacBook Pro
  • 良好的网络条件(你懂的)
  • 对 k8s 体系的概念有所了解, 不了解的话先跑起来慢慢补课也行

安装 minikube

官方安装教程: https://kubernetes.io/docs/tasks/tools/install-minikube/

mac 安装 minikube 很简单, 有 brew 包管理工具的话一行命令就搞定

brew install minikube

可能有同学会说,
我的 brew 怎么这么慢呀?
换国内镜像源呀!
换了镜像源怎么还是慢呀?
...

其实笔者并不推荐切换到国内镜像源, 老老实实给终端设置代理比较好,为啥呢?因为切换软件源只能提升软件源列表更新的速度,而大部分软件的真实安装包文件还是从国外下载的, 相当一部分软件的安装包都在 github, github 的 release 文件都放在 aws s3 上, s3 又被墙了,所以... 有一个稳定的代理是相当重要滴,只需要给终端添加一下环境变量就 ok 啦

export HTTP_PROXY=http://192.160.20.147:1087
export HTTPS_PROXY=http://192.160.20.147:1087
export ALL_PROXY=socks5://192.160.20.147:1086

emmm 跑题了

安装后可以检查一下版本

➜  ~ minikube version
minikube version: v1.12.3
commit: 2243b4b97c131e3244c5f014faedca0d846599f5

安装 VirtualBox

咦~ 为啥还要安装 VirtualBox? 因为 minikube 原理就是帮你维护一个虚拟机, 在虚拟机里再帮你搭建 k8s 环境

不用虚拟机也可以,minikube 也支持在 docker 容器里帮你搞一套环境,不过鉴于 mac 版 docker 的辣鸡性能还是选 VirtualBox 吧...

支持的列表有这么多, 不喜欢 VirtualBox 的话自己挑一个

*   docker ([driver installation](https://minikube.sigs.k8s.io/docs/drivers/docker/))
*   virtualbox ([driver installation](https://minikube.sigs.k8s.io/docs/drivers/virtualbox/))
*   podman ([driver installation](https://minikube.sigs.k8s.io/docs/drivers/podman/)) (EXPERIMENTAL)
*   vmwarefusion
*   kvm2 ([driver installation](https://minikube.sigs.k8s.io/docs/reference/drivers/kvm2/))
*   hyperkit ([driver installation](https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/))
*   hyperv ([driver installation](https://minikube.sigs.k8s.io/docs/reference/drivers/hyperv/)) Note that the IP below is dynamic and can change. It can be retrieved with `minikube ip`.
*   vmware ([driver installation](https://minikube.sigs.k8s.io/docs/reference/drivers/vmware/)) (VMware unified driver)
*   parallels ([driver installation](https://minikube.sigs.k8s.io/docs/reference/drivers/parallels/))
*   none (Runs the Kubernetes components on the host and not in a virtual machine. You need to be running Linux and to have [Docker](https://docs.docker.com/engine/) installed.)

开动 minikube

开动 minikube 之前别忘了设置代理,可以省很多麻烦, 不然下载时卡着不动很烦的

export HTTP_PROXY=http://192.160.20.147:1087
export HTTPS_PROXY=http://192.160.20.147:1087
export ALL_PROXY=socks5://192.160.20.147:1087
export NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,192.168.99.0/24,192.168.39.0/24
NO_PROXY 也很重要, 不然内部访问内部服务还要走代理的话会不通

接下来就可以开动了, 由于第一次运行下载的东西比较多, 即使上代理也会慢,要耐心等待

➜  ~ minikube start --vm-driver=virtualbox
😄  Darwin 10.15.6 上的 minikube v1.12.3
✨  根据用户配置使用 virtualbox 驱动程序
💿  正在下载 VM boot image...
    > minikube-v1.12.2.iso.sha256: 65 B / 65 B [-------------] 100.00% ? p/s 0s
    > minikube-v1.12.2.iso: 173.73 MiB / 173.73 MiB [ 100.00% 2.77 MiB p/s 1m3s
👍  Starting control plane node minikube in cluster minikube
💾  Downloading Kubernetes v1.18.3 preload ...
    > preloaded-images-k8s-v5-v1.18.3-docker-overlay2-amd64.tar.lz4: 104.78 MiB
🔥  Creating virtualbox VM (CPUs=2, Memory=6000MB, Disk=20000MB) ...
🌐  找到的网络选项:
    ▪ HTTP_PROXY=http://192.160.20.147:1087
    ▪ HTTPS_PROXY=http://192.160.20.147:1087
    ▪ NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,192.168.99.0/24,192.168.39.0/24
🐳  正在 Docker 19.03.12 中准备 Kubernetes v1.18.3…
    ▪ env HTTP_PROXY=http://192.160.20.147:1087
    ▪ env HTTPS_PROXY=http://192.160.20.147:1087
    ▪ env NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,192.168.99.0/24,192.168.39.0/24
    > kubelet.sha256: 65 B / 65 B [--------------------------] 100.00% ? p/s 0s
    > kubeadm.sha256: 65 B / 65 B [--------------------------] 100.00% ? p/s 0s
    > kubectl.sha256: 65 B / 65 B [--------------------------] 100.00% ? p/s 0s
    > kubeadm: 37.97 MiB / 37.97 MiB [------------] 100.00% 1020.00 KiB p/s 38s
    > kubectl: 41.99 MiB / 41.99 MiB [------------] 100.00% 704.44 KiB p/s 1m1s
    > kubelet: 108.04 MiB / 108.04 MiB [-----------] 100.00% 1.48 MiB p/s 1m14s
🔎  Verifying Kubernetes components...
🌟  Enabled addons: default-storageclass, storage-provisioner
🏄  完成!kubectl 已经配置至 "minikube"

下面这个启动命令是从网上找的,会用 docker 来当驱动, 不科学上网也可以很快启动, 不想折腾的可以用这个, 不过要是想在里面再安装 istio 可能会费点劲,贴出来仅供参考。

minikube start --registry-mirror=https://registry.docker-cn.com --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers --vm-driver=docker --base-image registry.cn-hangzhou.aliyuncs.com/google_containers/kicbase:v0.0.10

安装后可以运行下面的命令检查一下状态

➜  ~ kubectl get nodes
NAME       STATUS   ROLES    AGE    VERSION
minikube   Ready    master   108s   v1.18.3

kubernetes dashboard

既然已经跑起来,就像看点画面, k8s 提供了一个中控台, minikube 可以很方便的打开这个页面

➜  ~ minikube dashboard

🔌  正在开启 dashboard ...
🤔  正在验证 dashboard 运行情况 ...
🚀  Launching proxy ...
🤔  正在验证 proxy 运行状况 ...
🎉  Opening http://127.0.0.1:53569/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...
终端不要关闭,想要继续学习新开个终端 tab

运行后就会帮你在默认浏览器打开一个页面,大概长这个样子, 暂时不做研究,可以随便点一点看一看

image.png

学习任务东西都免不了要来个 hello word, 下面我们在 k8s 里面启动一个 web 服务, 请求的话会回显我们的 http 请求头信息

启动命令

kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.10

检查命令

kubectl get pods

如果结果像我这样就代表成功了

➜  ~ kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.10
deployment.apps/hello-minikube created
➜  ~ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
hello-minikube-64b64df8c9-d5lp5   1/1     Running   0          67s

如果 pods 的状态是 ContainerCreating, 可以稍等一下再运行一次命令, Running 才算成功, 还不行的话,可以用 kubectl logs hello-minikube-64b64df8c9-d5lp5 查看日志。注意你的 name 跟我的可能不一样。

到这里,我们已经启动了一个 web 应用, 但是在外部还不能访问, 需要把它当做服务去公开

kubectl expose deployment hello-minikube --type=NodePort --port=8080

minikube service hello-minikube --url

现在控制台就会打印出一个可以访问的 URl, 例如我的是:http://192.168.99.105:30120
里面会显出出浏览器的 user-agent 和其他信息

image.png

体验伸缩服务

现在多次访问 web 服务, 响应中的 Hostname 都不会变, 因为只运行了一个 web 服务, pod 数量是1.

工作中为了高可用一份服务都会部署多套, k8s 尤其擅长这种事,我们可以一键扩缩容。

kubectl scale deployment hello-minikube --replicas=3

执行后 k8s 就会开始进行调度,保证服务有3个实例,多退少补

➜  ~ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
hello-m inikube-64b64df8c9-6tqjv   1/1     Running   0          3m11s
hello-minikube-64b64df8c9-d5lp5   1/1     Running   0          36m
hello-minikube-64b64df8c9-j77fq   1/1     Running   0          3m11s

再次访问http://192.168.99.105:30120 频繁刷新下, 就会发现 Hostname 有所改变,说明我们的请求被随机分配到了 3 个 pod 中, 由多个实例来进行处理。

关闭 minikube

实验完毕可以用 minikube stop 关闭 minikube 下次可以直接用 minikube start 启动, 不想要的话还可以 minikube delete 删除

查看原文

赞 0 收藏 0 评论 0

junbaor 赞了问题 · 2019-11-15

解决用xauth认证获取到的AccessToken请求api不成功

用的是这里的开源项目https://github.com/Mashape/ma...
应该是这一步出错了

oa.post(options, callback);
oa.get(options, callback);
oa.delete(options, callback);
oa.patch(options, callback);
oa.put(options, callback);

// Alternatively, you can use the old node-oauth style: (Where method is one of five above.)
oa.method(url, oauth_token, oauth_token_secret, body, type, parameters, callback);

获取到了acess_token,但是调取api的时候报错"error":"Invalid access token",求大神帮忙看一下 感激不尽

var express = require('express');
    var router = express.Router();
    var OAuth = require('mashape-oauth').OAuth;
    var consumerKey    = 'myconsumerKey';
    var consumerSecret = 'myconsumerSecret';
    var oa = new OAuth({ 
      accessUrl:"http://fanfou.com/oauth/request_token",
      consumerKey:consumerKey,
      consumerSecret:consumerSecret,
      version:'1.0',
      signatureMethod:'HMAC-SHA1'
    });
    
    /* GET home page. */
    router.get('/', function(req, res, next) {
      res.render('index', { title: 'hello' });
    });
    router.route("/fanfou").get(function(req,res){   
        res.render("fanfou",{title:'fanfou'});
    }).post(function(req,res){                        
        var username = req.body.username;  
        var password= req.body.password;
        oa.getXAuthAccessToken(username, password, function (error, oauth_token, oauth_token_secret, results) {
          if (error)
            console.log('err');
          else
          {
          /* res.json({
            oauth_token:oauth_token,
            oauth_token_secret:oauth_token_secret
           })*/
           oa.get('http://api.fanfou.com/statuses/home_timeline.json',
           oauth_token,oauth_token_secret,null,null,null,function(error,data){
                   res.json({
                     data:data
                   });
                   //console.log(JSON.parse(data));
           }); 
          }
    
        });
      
    });
    module.exports = router;

关注 3 回答 2

junbaor 收藏了问题 · 2019-11-15

用xauth认证获取到的AccessToken请求api不成功

用的是这里的开源项目https://github.com/Mashape/ma...
应该是这一步出错了

oa.post(options, callback);
oa.get(options, callback);
oa.delete(options, callback);
oa.patch(options, callback);
oa.put(options, callback);

// Alternatively, you can use the old node-oauth style: (Where method is one of five above.)
oa.method(url, oauth_token, oauth_token_secret, body, type, parameters, callback);

获取到了acess_token,但是调取api的时候报错"error":"Invalid access token",求大神帮忙看一下 感激不尽

var express = require('express');
    var router = express.Router();
    var OAuth = require('mashape-oauth').OAuth;
    var consumerKey    = 'myconsumerKey';
    var consumerSecret = 'myconsumerSecret';
    var oa = new OAuth({ 
      accessUrl:"http://fanfou.com/oauth/request_token",
      consumerKey:consumerKey,
      consumerSecret:consumerSecret,
      version:'1.0',
      signatureMethod:'HMAC-SHA1'
    });
    
    /* GET home page. */
    router.get('/', function(req, res, next) {
      res.render('index', { title: 'hello' });
    });
    router.route("/fanfou").get(function(req,res){   
        res.render("fanfou",{title:'fanfou'});
    }).post(function(req,res){                        
        var username = req.body.username;  
        var password= req.body.password;
        oa.getXAuthAccessToken(username, password, function (error, oauth_token, oauth_token_secret, results) {
          if (error)
            console.log('err');
          else
          {
          /* res.json({
            oauth_token:oauth_token,
            oauth_token_secret:oauth_token_secret
           })*/
           oa.get('http://api.fanfou.com/statuses/home_timeline.json',
           oauth_token,oauth_token_secret,null,null,null,function(error,data){
                   res.json({
                     data:data
                   });
                   //console.log(JSON.parse(data));
           }); 
          }
    
        });
      
    });
    module.exports = router;

junbaor 关注了用户 · 2019-11-10

JerryTse @jerrytse

关注 55

junbaor 关注了专栏 · 2018-09-30

JavaGuide

主Java,平时也会发一些新技术比如Python的奇能技巧,机器学习,大数据,区块链等等。 在这里会定期更新文章。 欢迎关注我的微信公众号:Java面试通关手册。收获更多知识和免费资源。

关注 932

junbaor 关注了问题 · 2018-09-28

解决cms yonggc频率突然暴增,cpu被打满---如何定位是什么原因导致的这个现象?

1.昨天中午服务器监控提示:服务器xx.xx.xx.xx机器cpu使用率连续36次超过20%
2.服务器基础信息:javaweb项目,提供查询,导出等功能。
3.监控信息如下:
yonggc监控:

clipboard.png
堆内存监控:

clipboard.png
cpu监控:

clipboard.png
full-gc监控:

clipboard.png
机器运行实例监控

clipboard.png

当时机器的gclog:
以上是监控信息~
由于其他同事在14:32时手动执行了一次fullgc导致,内存的一回收,才恢复平静。
当时,启动参数:-Xms4g -Xmx4g -XX:PermSize=300m -XX:MaxPermSize=300m -XX:NewSize=2g -XX:MaxNewSize=2g -XX:SurvivorRatio=20 -XX:MaxTenuringThreshold=10 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=4 -XX:+CMSScavengeBeforeRemark -XX:+CMSParallelRemarkEnabled -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingPermOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrent -XX:CMSFullGCsBeforeCompaction=5 -XX:+CMSClassUnloadingEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=30 -Xloggc:/tomcat7-gc-cms.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintReferenceGC -XX:+PrintTenuringDistribution
通过查看tomcat7-gc-cms.log分析过程:
14:23有一次fullgc log位置:
fullgc--start

2018-04-16T14:23:45.747+0800: 356595.145: [CMS-concurrent-mark: 1.508/1.538 secs] [Times: user=12.08 sys=0.00, real=1.54 secs] 
2018-04-16T14:23:45.748+0800: 356595.145: [CMS-concurrent-preclean-start]
2018-04-16T14:23:45.748+0800: 356595.145: [Preclean SoftReferences, 0.0005960 secs]2018-04-16T14:23:45.748+0800: 356595.145: [Preclean WeakReferences, 0.0001810 secs]2018-04-16T14:23:45.748+0800: 356595.146: [Preclean FinalReferences, 0.0000650 secs]2018-04-16T14:23:45.748+0800: 356595.146: [Preclean PhantomReferences, 0.0000160 secs]2018-04-16T14:23:45.756+0800: 356595.153: [CMS-concurrent-preclean: 0.008/0.009 secs] [Times: user=0.07 sys=0.00, real=0.01 secs] 
2018-04-16T14:23:45.756+0800: 356595.154: [CMS-concurrent-abortable-preclean-start]
{Heap before GC invocations=652 (full 1):
 par new generation   total 2001856K, used 1915155K [0x00000006ed400000, 0x000000076d400000, 0x000000076d400000)
  eden space 1906560K, 100% used [0x00000006ed400000, 0x00000007619e0000, 0x00000007619e0000)
  from space 95296K,   9% used [0x00000007619e0000, 0x0000000762244da8, 0x00000007676f0000)
  to   space 95296K,   0% used [0x00000007676f0000, 0x00000007676f0000, 0x000000076d400000)
 concurrent mark-sweep generation total 2097152K, used 629223K [0x000000076d400000, 0x00000007ed400000, 0x00000007ed400000)
 concurrent-mark-sweep perm gen total 307200K, used 143890K [0x00000007ed400000, 0x0000000800000000, 0x0000000800000000)
2018-04-16T14:23:45.976+0800: 356595.373: [GC2018-04-16T14:23:45.976+0800: 356595.373: [ParNew2018-04-16T14:23:45.988+0800: 356595.385: [SoftReference, 0 refs, 0.0000510 secs]2018-04-16T14:23:45.988+0800: 356595.385: [WeakReference, 25 refs, 0.0000140 secs]2018-04-16T14:23:45.988+0800: 356595.385: [FinalReference, 45 refs, 0.0000290 secs]2018-04-16T14:23:45.988+0800: 356595.385: [PhantomReference, 0 refs, 0.0000100 secs]2018-04-16T14:23:45.988+0800: 356595.385: [JNI Weak Reference, 0.0000100 secs]
Desired survivor size 48791552 bytes, new threshold 10 (max 10)
- age   1:    3537312 bytes,    3537312 total
- age   2:    1668536 bytes,    5205848 total
- age   3:     337152 bytes,    5543000 total
- age   4:      14496 bytes,    5557496 total
- age   5:      16456 bytes,    5573952 total
- age   6:      10904 bytes,    5584856 total
- age   7:      12664 bytes,    5597520 total
- age   8:      14056 bytes,    5611576 total
- age   9:      11808 bytes,    5623384 total
- age  10:      18184 bytes,    5641568 total
: 1915155K->8115K(2001856K), 0.0122020 secs] 2544378K->637347K(4099008K), 0.0124670 secs] [Times: user=0.04 sys=0.00, real=0.01 secs] 
Heap after GC invocations=653 (full 1):
 par new generation   total 2001856K, used 8115K [0x00000006ed400000, 0x000000076d400000, 0x000000076d400000)
  eden space 1906560K,   0% used [0x00000006ed400000, 0x00000006ed400000, 0x00000007619e0000)
  from space 95296K,   8% used [0x00000007676f0000, 0x0000000767edcca0, 0x000000076d400000)
  to   space 95296K,   0% used [0x00000007619e0000, 0x00000007619e0000, 0x00000007676f0000)
 concurrent mark-sweep generation total 2097152K, used 629232K [0x000000076d400000, 0x00000007ed400000, 0x00000007ed400000)
 concurrent-mark-sweep perm gen total 307200K, used 143890K [0x00000007ed400000, 0x0000000800000000, 0x0000000800000000)
}
2018-04-16T14:23:46.346+0800: 356595.743: [CMS-concurrent-abortable-preclean: 0.274/0.590 secs] [Times: user=4.61 sys=0.00, real=0.59 secs] 
2018-04-16T14:23:46.351+0800: 356595.748: [GC[YG occupancy: 1249269 K (2001856 K)]{Heap before GC invocations=653 (full 1):
 par new generation   total 2001856K, used 1249269K [0x00000006ed400000, 0x000000076d400000, 0x000000076d400000)
  eden space 1906560K,  65% used [0x00000006ed400000, 0x00000007390109d8, 0x00000007619e0000)
  from space 95296K,   8% used [0x00000007676f0000, 0x0000000767edcca0, 0x000000076d400000)
  to   space 95296K,   0% used [0x00000007619e0000, 0x00000007619e0000, 0x00000007676f0000)
 concurrent mark-sweep generation total 2097152K, used 629232K [0x000000076d400000, 0x00000007ed400000, 0x00000007ed400000)
 concurrent-mark-sweep perm gen total 307200K, used 143890K [0x00000007ed400000, 0x0000000800000000, 0x0000000800000000)
2018-04-16T14:23:46.351+0800: 356595.748: [GC2018-04-16T14:23:46.351+0800: 356595.748: [ParNew2018-04-16T14:23:46.363+0800: 356595.760: [SoftReference, 0 refs, 0.0000360 secs]2018-04-16T14:23:46.363+0800: 356595.760: [WeakReference, 24 refs, 0.0000140 secs]2018-04-16T14:23:46.363+0800: 356595.760: [FinalReference, 48 refs, 0.0000200 secs]2018-04-16T14:23:46.363+0800: 356595.760: [PhantomReference, 0 refs, 0.0000100 secs]2018-04-16T14:23:46.363+0800: 356595.760: [JNI Weak Reference, 0.0000090 secs]
Desired survivor size 48791552 bytes, new threshold 10 (max 10)
- age   1:    2807512 bytes,    2807512 total
- age   2:    1240272 bytes,    4047784 total
- age   3:     487568 bytes,    4535352 total
- age   4:      86712 bytes,    4622064 total
- age   5:       8384 bytes,    4630448 total
- age   6:      12944 bytes,    4643392 total
- age   7:       9152 bytes,    4652544 total
- age   8:      10872 bytes,    4663416 total
- age   9:      11560 bytes,    4674976 total
- age  10:       8456 bytes,    4683432 total
: 1249269K->5485K(2001856K), 0.0116790 secs] 1878501K->634726K(4099008K), 0.0119130 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] 
Heap after GC invocations=654 (full 1):
 par new generation   total 2001856K, used 5485K [0x00000006ed400000, 0x000000076d400000, 0x000000076d400000)
  eden space 1906560K,   0% used [0x00000006ed400000, 0x00000006ed400000, 0x00000007619e0000)
  from space 95296K,   5% used [0x00000007619e0000, 0x0000000761f3b450, 0x00000007676f0000)
  to   space 95296K,   0% used [0x00000007676f0000, 0x00000007676f0000, 0x000000076d400000)
 concurrent mark-sweep generation total 2097152K, used 629241K [0x000000076d400000, 0x00000007ed400000, 0x00000007ed400000)
 concurrent-mark-sweep perm gen total 307200K, used 143890K [0x00000007ed400000, 0x0000000800000000, 0x0000000800000000)
}
2018-04-16T14:23:46.363+0800: 356595.760: [Rescan (parallel) , 0.0037940 secs]2018-04-16T14:23:46.367+0800: 356595.764: [weak refs processing2018-04-16T14:23:46.367+0800: 356595.764: [SoftReference, 14978 refs, 0.0015960 secs]2018-04-16T14:23:46.368+0800: 356595.766: [WeakReference, 449 refs, 0.0000620 secs]2018-04-16T14:23:46.369+0800: 356595.766: [FinalReference, 1472 refs, 0.0051820 secs]2018-04-16T14:23:46.374+0800: 356595.771: [PhantomReference, 1 refs, 0.0000130 secs]2018-04-16T14:23:46.374+0800: 356595.771: [JNI Weak Reference, 0.0000100 secs], 0.0069140 secs]2018-04-16T14:23:46.374+0800: 356595.771: [class unloading, 0.0269340 secs]2018-04-16T14:23:46.401+0800: 356595.798: [scrub symbol table, 0.0164990 secs]2018-04-16T14:23:46.417+0800: 356595.815: [scrub string table, 0.0036960 secs] [1 CMS-remark: 629241K(2097152K)] 634726K(4099008K), 0.0799200 secs] [Times: user=0.12 sys=0.00, real=0.08 secs] 
2018-04-16T14:23:46.431+0800: 356595.828: [CMS-concurrent-sweep-start]
2018-04-16T14:23:46.802+0800: 356596.199: [CMS-concurrent-sweep: 0.363/0.371 secs] [Times: user=2.89 sys=0.00, real=0.37 secs] 
2018-04-16T14:23:46.802+0800: 356596.199: [CMS-concurrent-reset-start]
2018-04-16T14:23:46.835+0800: 356596.233: [CMS-concurrent-reset: 0.033/0.033 secs] [Times: user=0.27 sys=0.00, real=0.04 secs] 
**fullgc-end**
通过fullgc后的一次yonggc分析发现:
{Heap before GC invocations=654 (full 1):
 par new generation   total 2001856K, used 1912541K [0x00000006ed400000, 0x000000076d400000, 0x000000076d400000)
  eden space 1906560K, 100% used [0x00000006ed400000, 0x00000007619e0000, 0x00000007619e0000)
  from space 95296K,   6% used [0x00000007619e0000, 0x0000000761fb7548, 0x00000007676f0000)
  to   space 95296K,   0% used [0x00000007676f0000, 0x00000007676f0000, 0x000000076d400000)
 concurrent mark-sweep generation total 2097152K, used 259145K [0x000000076d400000, 0x00000007ed400000, 0x00000007ed400000)
 concurrent-mark-sweep perm gen total 307200K, used 143429K [0x00000007ed400000, 0x0000000800000000, 0x0000000800000000)
2018-04-16T14:23:47.037+0800: 356596.434: [GC2018-04-16T14:23:47.037+0800: 356596.434: [ParNew2018-04-16T14:23:47.049+0800: 356596.446: [SoftReference, 0 refs, 0.0000370 secs]2018-04-16T14:23:47.049+0800: 356596.446: [WeakReference, 26 refs, 0.0000130 secs]2018-04-16T14:23:47.049+0800: 356596.446: [FinalReference, 43 refs, 0.0000470 secs]2018-04-16T14:23:47.049+0800: 356596.446: [PhantomReference, 0 refs, 0.0000090 secs]2018-04-16T14:23:47.049+0800: 356596.446: [JNI Weak Reference, 0.0000100 secs]
Desired survivor size 48791552 bytes, new threshold 10 (max 10)
- age   1:    4083168 bytes,    4083168 total
- age   2:    1029504 bytes,    5112672 total
- age   3:     362720 bytes,    5475392 total
- age   4:      28200 bytes,    5503592 total
- age   5:      56616 bytes,    5560208 total
- age   6:       8384 bytes,    5568592 total
- age   7:      12944 bytes,    5581536 total
- age   8:       7328 bytes,    5588864 total
- age   9:       8472 bytes,    5597336 total
- age  10:      11560 bytes,    5608896 total
: 1912541K->8400K(2001856K), 0.0122020 secs] **2171686K->267554K**(4099008K), 0.0124480 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
Heap after GC invocations=655 (full 1):
 par new generation   total 2001856K, used 8400K [0x00000006ed400000, 0x000000076d400000, 0x000000076d400000)
  eden space 1906560K,   0% used [0x00000006ed400000, 0x00000006ed400000, 0x00000007619e0000)
  from space 95296K,   8% used [0x00000007676f0000, 0x0000000767f24158, 0x000000076d400000)
  to   space 95296K,   0% used [0x00000007619e0000, 0x00000007619e0000, 0x00000007676f0000)
 concurrent mark-sweep generation total 2097152K, used 259153K [0x000000076d400000, 0x00000007ed400000, 0x00000007ed400000)
 concurrent-mark-sweep perm gen total 307200K, used 143429K [0x00000007ed400000, 0x0000000800000000, 0x0000000800000000)
}

老年代确实降下来了:2171686K->267554K
但是新生代,为何频繁回收呢?
当时线程gclog-链接: https://pan.baidu.com/s/1jz5I... 密码: ddrz

暂时自己的解决办法是扩大新生代为3g
有没有遇到过这类问题的?

关注 7 回答 4

junbaor 赞了文章 · 2018-09-28

Java Flight Recorder小试牛刀

本文主要研究一下Java Flight Recorder的使用。

命令

主要有5个命令,configure、check、start、dump、stop。执行顺序的话,先start再dump,最后stop。

JFR.configure

参数描述值类型默认值
globalbuffercount指定global buffers的数量. 修改 memorysize参数会影响该值.Long默认值依赖 memorysize 参数.
globalbuffersize指定global buffers大小, 单位bytes. 修改 memorysize 参数会影响到global buffers.Long默认值依赖 memorysize 参数.
maxchunksize指定单个data chunk的最大值, 单位bytesLong12582912
memorysize指定总内存大小, 单位bytesLong10485760
repositorypath指定recordings在写入到持久化文件之前的存储路径String默认为系统临时目录,Oracle Solaris以及Linux是/tmp.windows系统的话,取TMP环境变量值
stackdepth指定stack traces的Stack depthLong64
thread_buffer_size指定每个thread的Local buffer size, 单位bytes. 不建议修改此参数,可能会降低性能Long8192
threadbufferstodisk是否允许thread buffers在buffer thread阻塞的时候直接写到磁盘Booleanfalse
samplethreads是否开启thread samplingBooleantrue
命令实例
jcmd 5793 JFR.configure
5793:
Current configuration:

Repository path: /private/var/folders/9r/v55wkcr91m5_g8h7lhgjzgr00000gn/T/2018_09_27_16_30_53_5793

Stack depth: 64
Global buffer count: 20
Global buffer size: 512.0 kB
Thread buffer size: 8.0 kB
Memory size: 10.0 MB
Max chunk size: 12.0 MB
Sample threads: true

JFR.start

参数描述值类型默认值
delay指定延时多长时间才开始记录Integer类型加s表示秒, m表示分钟, 或者h表示小时0s
disk记录的时候是否写数据到磁盘Booleantrue
dumponexit是否在JVM关闭时写记录到磁盘. 如果为true但没有指定filename, 则文件名为系统生成,包含process ID, recording ID,以及 current time stamp (例如,hotspot-pid-47496-id-1-2018_01_25_19_10_41.jfr),文件路径为进程启动路径 .Booleanfalse
duration指定记录时长Integer类型加s表示秒, m表示分钟, 或者h表示小时0s (forever)
filename指定停止时记录数据的文件路径,如果未指定,则使用进程使用目录,例如recording.jfr`/home/user/recordings/recording.jfr`c:\recordings\recording.jfrStringNo default value
maxage指定记录数据在磁盘的最大存活时间,当disk参数为true时才有效Integer类型加s表示秒, m表示分钟, 或者h表示小时0s (forever)
maxsize指定记录数据在磁盘的最大大小,默认单位bytes,指定mM表示兆,gG表示G,只有当disk参数为true时才有效,该值不能比maxchunksize参数值小.Long0 (no maximum size)
name指定记录文件名,如未指定则默认生成.String默认为系统生成.
path-to-gc-rootsJDK 10引入的,指定在记录结束前要收集的GC Roots的路径.该参数有助于排查内存泄露,但是收集比较耗时,当且仅当怀疑有内存泄露时才启用。如果settings参数设置为profile, 则收集的信息包括潜在内存泄露对象的stack trace.Booleanfalse
settings指定记录的配置文件,如果不是JRE_HOME/lib/jfr目录下的要指定全路径,要指定多个的话,用逗号分隔。默认路径有default.jfc: 该配置开销低,可以用于持续运行.profile.jfc: 则提供比default更多的数据,但是开销大一些,对性能有所影响,适合短时间收集信息用StringJRE_HOME/lib/jfr/default.jfc
命令实例
jcmd 5793 JFR.start name=demojfr dumponexit=true
5793:
Started recording 1. No limit specified, using maxsize=250MB as default.

Use jcmd 5793 JFR.dump name=demojfr filename=FILEPATH to copy recording data to file.

JFR.check

参数描述值类型默认值
name指定文件名StringNo default value
verbose是否打印event settingsBooleanfalse
命令实例
jcmd 5793 JFR.check
5793:
Recording 1: name=demojfr maxsize=250.0MB (running)

JFR.dump

参数描述值类型默认值
filename(required)指定dump写入的路径,如果未指定,则使用进程启动的目录,例如:recording.jfr`/home/user/recordings/recording.jfr`c:\recordings\recording.jfrStringNo default value
name(required)指定要dump的记录StringNo default value
path-to-gc-rootsJDK 10引入的,指定在记录结束前要收集的GC Roots的路径.该参数有助于排查内存泄露,但是收集比较耗时,当且仅当怀疑有内存泄露时才启用.Booleanfalse
命令实例
jcmd 5793 JFR.dump name=demojfr filename=/tmp/demo.jfr
5793:
Dumped recording "demojfr", 480.8 kB written to:

/tmp/demo.jfr

JFR.stop

参数描述值类型默认值
filename指定停止时数据写入的路径.如果没有指定则默认为进程启动的目录,例如recording.jfr`/home/user/recordings/recording.jfr`c:\recordings\recording.jfrStringNo default value
name指定要stop的记录的名称StringNo default value
命令实例
jcmd 5793 JFR.stop name=demojfr
5793:
Stopped recording "demojfr".

JMC

JMC打开jfr文件实例截图如下:
图片描述

读取JFR文件

    @Test
    public void testReadJfr() throws IOException {
        Path p = Paths.get(getClass().getClassLoader().getResource("demo.jfr").getPath());
        List<RecordedEvent> events = RecordingFile.readAllEvents(p);
        events.stream()
                .forEach(e -> LOGGER.info("eventType:{},startTime:{},endTime:{},fields:{}",e.getEventType().getName(),e.getStartTime(),e.getEndTime(),e.getFields()));
        List<String> eventNames = events.stream()
                .map(e -> e.getEventType().getName())
                .distinct()
                .collect(Collectors.toList());
        System.out.println(eventNames.toString());
    }
  • 直接使用jdk的api即可以解析jfr文件,读出RecordedEvent
  • eventType类型输出如下:
[jdk.ExceptionStatistics, jdk.NativeMethodSample, jdk.ThreadSleep, jdk.JavaMonitorWait, jdk.CPULoad, jdk.JavaThreadStatistics, jdk.ClassLoadingStatistics, jdk.CompilerStatistics, jdk.ClassLoaderStatistics, jdk.ModuleExport, jdk.CodeCacheStatistics, jdk.CodeSweeperStatistics, jdk.GCConfiguration, jdk.ActiveSetting, jdk.ActiveRecording, jdk.InitialSystemProperty, jdk.InitialEnvironmentVariable, jdk.CPUInformation, jdk.CPUTimeStampCounter, jdk.ThreadAllocationStatistics, jdk.PhysicalMemory, jdk.NativeLibrary, jdk.CompilerConfiguration, jdk.CodeCacheConfiguration, jdk.CodeSweeperConfiguration, jdk.IntFlag, jdk.UnsignedIntFlag, jdk.LongFlag, jdk.UnsignedLongFlag, jdk.DoubleFlag, jdk.BooleanFlag, jdk.StringFlag, jdk.ThreadEnd, jdk.ThreadCPULoad, jdk.NetworkUtilization, jdk.ThreadStart, jdk.ThreadContextSwitchRate, jdk.GCSurvivorConfiguration, jdk.GCTLABConfiguration, jdk.GCHeapConfiguration, jdk.YoungGenerationConfiguration, jdk.SystemProcess, jdk.ThreadDump, jdk.JVMInformation, jdk.OSInformation, jdk.ModuleRequire]
除了系统定义的eventType,还可以自定义event

自定义event

  • DemoEvent
@Label("Demo Event")
@Description("Helps the programmer getting started")
public class DemoEvent extends Event {
    @Label("Message")
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
  • 发布事件
        DemoEvent event = new DemoEvent();
        event.setMessage("hello, world!");
        event.commit();
之后使用api解析jfr文件,可以看到自定义的event,其name为com.example.jfr.DemoEvent
可以使用如下参数启动-XX:StartFlightRecording=duration=120s,filename=/tmp/event.jfr,settings=default,name=DemoEventRecording

相关模块

JDK11关于jfr的模块有两个,分别是jdk.jfr.jmod以及jdk.management.jfr.jmod,其具体内容如下:

➜  jmods ../bin/jmod describe jdk.jfr.jmod
jdk.jfr@11
exports jdk.jfr
exports jdk.jfr.consumer
requires java.base mandated
qualified exports jdk.jfr.internal.management to jdk.management.jfr
contains jdk.jfr.events
contains jdk.jfr.internal
contains jdk.jfr.internal.cmd
contains jdk.jfr.internal.consumer
contains jdk.jfr.internal.dcmd
contains jdk.jfr.internal.handlers
contains jdk.jfr.internal.instrument
contains jdk.jfr.internal.jfc
contains jdk.jfr.internal.settings
contains jdk.jfr.internal.test
contains jdk.jfr.internal.types
platform macos-amd64

➜  jmods ../bin/jmod describe jdk.management.jfr.jmod
jdk.management.jfr@11
exports jdk.management.jfr
requires java.base mandated
requires java.management transitive
requires jdk.jfr
requires jdk.management
provides sun.management.spi.PlatformMBeanProvider with jdk.management.jfr.internal.FlightRecorderMXBeanProvider
contains jdk.management.jfr.internal
platform macos-amd64

小结

  • Java Flight Recorder是一款优秀的java应用诊断工具,以前是商业版的特性,现在在java11当中开源出来,它导出的jfr文件可以用Java Mission Control来分析。
  • JDK11内置了相关API,可以用来解析jfr文件,也可以在应用程序自定义事件发布出来
  • JFR可以采用JVM命令启动,也可以使用jcmd的JFR.开头的命令在运行时操作,非常方便

doc

查看原文

赞 5 收藏 3 评论 0

junbaor 评论了文章 · 2018-09-25

Mastodon 生产部署指南

本问翻译自:https://github.com/tootsuite/... 由于版本迭代更新,本文档可能会落后, 有能力的话推荐阅读英文原文

Mastodon 生产指南

免责声明:

本指南是针对 Ubuntu Server 16.04 编写的, 如果使用其他操作系统,则可能会遇到问题。我们欢迎对其他发行版的指南作出贡献。

此文档希望你有足够高的技术管理 Linux 服务器。

这本指南是什么?

本指南介绍了 Mastodon 实例的部署过程。

我们用 example.com 表示域名或者子域名。 example.com 应该替换成你自己的域名或者子域名。

先决条件

本指南需要以下内容:

DNS

在服务器上完成任何操作之前,应先添加 DNS 记录。

添加的记录:

  • A 记录 (IPv4 地址) 解析到 example.com
  • AAAA 记录 (IPv6 地址) 解析到 example.com

一个有用但是非必须的提醒

使用 tmux 将会对本指南有所帮助。

不仅如此, 如果您断开连接, 这不仅可以帮助您找回失去的位置, 还可以打开多个终端窗口, 用来切换上下文 (root 用户和 mastodon 用户).

你可以从包管理器安装 tmux :

apt -y install tmux

依赖安装

所有的依赖项都应该以 root 身份安装.

node.js 仓库

您需要添加一个外部存储库,以便我们可以使用需要的 node.js 版本。

我们运行此脚本来添加存储库:

apt -y install curl
curl -sL https://deb.nodesource.com/setup_6.x | bash -

这个 node.js 存储库已经添加完成.

Yarn 仓库

需要添加另一个存储库, 以便我们可以获得 Mastodon 使用的 Yarn 版本.

你应该这样添加存储库:

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
apt update

各种其他依赖

现在你需要安装 Yarn 加上一些更多的软件.

依赖关系说明

  • imagemagick - Mastodon 使用 imagemagick 进行图像相关操作
  • ffmpeg - Mastodon 使用 ffmpeg 将 GIF 转换为 MP4
  • libprotobuf-dev 和 protobuf-compiler - Mastodon 用他们进行语言检测
  • nginx - nginx 是我们的前端 Web 服务器
  • redis-* - Mastodon 使用 redis 所谓其内存数据结构存储
  • postgresql-* - Mastodon 使用 PostgreSQL 作为 SQL 数据库
  • nodejs - Node 用于 Mastodon 的 流式 API
  • yarn - Yarn 是 Node.js 的包管理器
  • 其他的 -dev 包, g++ - 这些是使用 ruby-build 编译 Ruby 所需要的.
apt -y install imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev file git-core g++ libprotobuf-dev protobuf-compiler pkg-config nodejs gcc autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm3 libgdbm-dev nginx redis-server redis-tools postgresql postgresql-contrib letsencrypt yarn libidn11-dev libicu-dev

需要非 root 用户添加的依赖关系

首先创建这个用户:

adduser mastodon

mastodon 用户身份登录:

sudo su - mastodon

我们需要设置 rbenvruby-build:

git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
# 重启 shell
exec bash
# 检查 rbenv 是否正确安装
type rbenv
# 安装 ruby-build 为 rbenv 插件
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

现在 rbenvruby-build 已经安装成功, 我们需要安装
Mastodon 使用的 Ruby 版本。这个版本也需要安装启用。

要启用 Ruby, 请运行:

rbenv install 2.5.0
rbenv global 2.5.0

这将需要一些时间. 在命令运行时, 伸展一下, 喝点水.

node.js 和 Ruby 依赖

现在 Ruby 已启用, 我们将克隆 Mastodon git 仓库 并安装 Rubynode.js 依赖.

运行以下命令克隆并安装:

# 返回到 mastodon 用户的家目录
cd ~
# 克隆 mastodon git 仓库到 ~/live
git clone https://github.com/tootsuite/mastodon.git live
# 改变当前目录到 ~live
cd ~/live
# 迁出 mastodon 最后一个稳定 tag 版本
git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)
# 安装 bundler
gem install bundler
# 使用 bundler 安装其余的 Ruby 依赖项
bundle install --deployment --without development test
# 使用 yarn 安装 node.js 依赖项
yarn install --pure-lockfile

以上就是 mastodon 用户现在所需要做的, 你可以 exit 返回到 root 用户.

创建 PostgreSQL 数据库

Mastodon 需要访问 PostgreSQL 实例.

PostgreSQL 实例创建一个用户:

# 用 postgres 用户启动 psql
sudo -u postgres psql

# 跟从提示
CREATE USER mastodon CREATEDB;
\q

请注意 我们不设置任务形式的密码, 这是因为我们将使用 ident 身份验证. 允许本地在没有密码的情况下访问数据库.

nginx 配置

你需要配置 nginx 来访问 Mastodon 实例.

提醒: 将出现的 example.com 替换为你自己实例的域名或者子域名

cd/etc/nginx/sites-available 并打开一个新的文件:

nano /etc/nginx/sites-available/example.com.conf

复制粘贴一下内容并进行必要的修改:

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name example.com;
  # Useful for Let's Encrypt
  location /.well-known/acme-challenge/ { allow all; }
  location / { return 301 https://$host$request_uri; }
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name example.com;

  ssl_protocols TLSv1.2;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /home/mastodon/live/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  location / {
    try_files $uri @proxy;
  }

  location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    try_files $uri @proxy;
  }
  
  location /sw.js {
    add_header Cache-Control "public, max-age=0";
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";

    proxy_pass http://127.0.0.1:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}

激活添加的 nginx 配置:

cd /etc/nginx/sites-enabled
ln -s ../sites-available/example.com.conf

此配置假设您正在使用 Let's Encrypt 作为您的 TLS 证书提供程序.

如您要使用 Let's Encrypt 作为您的 TLS 证书提供者, 请参阅下一个子部分. 否则请编辑 ssl_certificatessl_certificate_key 为相应的值.

Let's Encrypt

如果您使用 Let's Encrypt 作为您的 TLS 证书提供者, 则此部分与您相关.

生成证书

我们需要生成 Let's Encrypt 证书.

确保 'example.com' 替换为 Mastodon 实例的域名.

确保此时 nginx 已停止:

systemctl stop nginx

我们将创建证书, 一次在独立模式下使用 TLS SNI 验证, 第二次使用 webroot 方法. 由于
nginxLet's Encrypt 的工作原理, 这是必须的.

letsencrypt certonly --standalone -d example.com

成功完成后, 我们将使用 webroot 方法. nginx 需要处于运行状态:

systemctl start nginx
# letsencrypt 工具将询问您是否要发出新的证书, 请选择该选项
letsencrypt certonly --webroot -d example.com -w /home/mastodon/live/public/

自动更新 Let's Encrypt 证书

Let's Encrypt 证书的有效期为 90 天.

您需要在到期日期之前更新证书. 不这样做会使您实例的用户无法访问其他与您联合的实例.

我们可以创建一个每天运行的 cron 作业:

nano /etc/cron.daily/letsencrypt-renew

将此脚本复制并粘贴到该文件中:

#!/usr/bin/env bash
letsencrypt renew
systemctl reload nginx

保存文件并推出.

该脚本添加执行权限并重新启动 cron 守护程序, 以便脚本每天运行:

chmod +x /etc/cron.daily/letsencrypt-renew
systemctl restart cron

就是这样. 您的服务器将续订您的 Let's Encrypt 证书.

Mastodon 应用配置

我们将配置 Mastodon 应用程序.

为此我们切换到 mastodon 系统用户:

sudo su - mastodon

将当前目录更改为 ~live 并编辑 Mastodon 应用配置:

cd ~/live
cp .env.production.sample .env.production
RAILS_ENV=production bundle exec rake mastodon:setup

会有一个交互的向导引导你完成基本和必要的选项设置, 生成新的应用 secrets, 设置数据库并编译资源.

资源预编译需要几分钟, 所以这是休息一下的好时机.

Mastodon 系统服务文件

我们需要为每个 Mastodon 服务提供三个 systemd 服务文件.

现在切换回 root 用户.

对于 Mastodon web 服务, 将以下内容放在 /etc/systemd/system/mastodon-web.service:

[Unit]
Description=mastodon-web
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb
ExecReload=/bin/kill -SIGUSR1 $MAINPID
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

对于 Mastodon 后台队列服务, 将以下内容放在 /etc/systemd/system/mastodon-sidekiq.service:

[Unit]
Description=mastodon-sidekiq
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

对于 Mastodon 流 API 服务, 将以下内容放在 /etc/systemd/system/mastodon-streaming.service:

[Unit]
Description=mastodon-streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/usr/bin/npm run start
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

现在您需要启用这些服务:

systemctl enable /etc/systemd/system/mastodon-*.service

现在启动服务:

systemctl start mastodon-*.service

检查它们是否正常运行:

systemctl status mastodon-*.service

就这些! 如果一切都正常完成, 当您在网络浏览器中访问 https://example.com 时, 会出现一个 Mastodon 实例.

祝贺你并欢迎来宾!

查看原文

junbaor 关注了专栏 · 2018-09-19

云巴专栏

云巴(Yunba.io)基于 MQTT,支持 Socket.IO 协议,支持 RESTful API,是一个跨平台的双向实时通信系统,为物联网、App 和 Web 提供实时通信服务。支持 C、Embeded C、iOS、Android 等多种 SDK。适用于 IM 即时通信、视频直播互动、物联网智能设备等场景。集成便捷,省时省力。 (官网:https://yunba.io/)

关注 29

junbaor 关注了问题 · 2018-09-09

idea如何debug调试动态生成的代理类?

idea如何调试动态生成的代理类?

最近在学习dubbo源码,经常会有部分功能是由动态代理生成的类来完成,如果按照正常的debug就只能跳入动态代理类实现的接口里,并不能看到实际做了什么,有没有什么插件或者技巧来处理这个情况?

关注 3 回答 0

认证与成就

  • 获得 107 次点赞
  • 获得 18 枚徽章 获得 1 枚金徽章, 获得 4 枚银徽章, 获得 13 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • mybatis-sharding

    一个简单的分表插件, 通过 mybatis 拦截器对原 SQL 进行改写. 利用 druid 数据源中的 sql 解析模块识别表名加后缀, 支持增、删、改、关联查询、子查询等其他常见操作. 典型的应用场景就是相同业务的表都要进行分表且后缀相同.

注册于 2016-03-08
个人主页被 1.5k 人浏览