抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前世今生

1410是刚毕业那会做一个毕业照展示网站项目。

早期使用的是Vue.js作为前端项目,Java作为后端项目。

中间将Python写的服务端替换了Java后端。

最后将Golang写的服务端替换了Python后端。

近期又做了一些前端方面的改造:

  • 使用authelia作为Nginx层的SSO网关,设置为登录可访问(不能实现全站CDN的原因是需要NGINX层)
  • 照片使用腾讯云CDN访问,并且为加快访问速度还使用了腾讯云的万象图片处理
  • 抛弃后端接口,直接使用json存储照片列表
  • 实现前端项目动静分离,引入CDN加快访问速度
  • 使用coding的持续集成实现自动化部署

以后再也不用操心改完代码还要做一堆的操作让代码上线了,网站访问速度和安全性都得到了提升。

升级打怪过程

这篇文章就主要介绍一下将vue项目build后实现动静分离和自动化部署过程,以及遇到的一些坑点。
以下是1410前端项目的项目结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|-- public //存放不可变静态资源
|-- index.html
|-- favicon.ico
|-- data.json
|-- src
|-- plugins //存放要引入的插件例如iview
|-- App.vue //项目入口
|-- main.js //项目配置
|-- config //存放打包成容器后的nginx的配置
|-- nginx.conf
|-- .env.deploy 打包时指定的文件
|-- index.js 上传打包后dist文件夹到COS
|-- Dockerfile 打包成docker镜像的配置
|-- Jenkinsfile 持续集成的配置文件
|-- vue.config.js vue的配置文件
|-- package.json //存放打包命令以及依赖
...

改造vue项目实现动静分离

如我上述的结构,改造vue项目非常简单,主要分为三步:

  1. 在根目录中创建.env.deploy文件,然后写入

    1
    2
    NODE_ENV=production
    DEPLOY=online
  2. 修改vue.config.js,将一下内容写入:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 根据自定义的变量来进行内容设置,将这块放在整个js文件的最上面即可
    let BASE_URL = '/'
    switch(process.env.DEPLOY) {
    case 'online':
    BASE_URL = 'https://cdn.songbo.fun/'
    break
    default:
    BASE_URL = '/'
    }
    然后在module.exports中增加一行,注意','
    publicPath: BASE_URL
  3. package.json中的scripts增加一行"deploy": "vue-cli-service build --mode deploy" 此处注意**,**

完成已上三步后就改造完成了,如果需要打包正常的项目使用npm run build 如果需要动静分离,使用npm run deploy 即可。
最终效果如下,在dist/index.html,对应的css地址就换成了如下的内容:

1
<link href=http://cdn.songbo.fun/static/css/app.887c93b2.css rel=preload as=style>

** 注意 这一步也没有遇到什么坑点,很快就完成了项目的动静分离。

实现自动化部署

上一步已经实现了打包的过程,初步具备了前端站点动静分离的条件,最终生成的dist目录结构如下:

1
2
3
4
5
|-- index.html   // 需要copy到docker镜像中
|-- static // 整个文件夹都需要放到COS上去,使用CDN访问
|-- js
|-- img
|-- css

自动化部署总体流程比较简单,分为如下几步:

  1. 选择一个devops服务提供商,我目前主要用两个:coding的持续集成以及github的action,配置起来都比较简单。
  2. 选定Docker镜像并且准备好对应的配置,我这里选用的是nginx的docker镜像,准备了config/nginx.conf文件和Dockerfile文件,对应的文件存放位置可以在上文中看到,文件具体内容如下:
    nginx.conf:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    # nginx.conf
    user nginx;
    worker_processes 1;

    error_log /var/log/nginx/error.log warn;
    pid /var/run/nginx.pid;

    events {
    worker_connections 1024;
    }

    http {

    map $http_x_forwarded_for $clientRealIp {
    "" $remote_addr;
    ~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
    }

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;

    gzip on;

    client_max_body_size 100m;
    client_body_buffer_size 10m;
    proxy_connect_timeout 600s;
    proxy_send_timeout 600s;
    proxy_read_timeout 600s;
    send_timeout 600s;

    proxy_request_buffering off;
    proxy_buffering off;

    server {
    listen 80;
    server_name localhost;

    charset utf-8;

    root /usr/share/nginx/html;

    location / {
    try_files $uri $uri/ /index.html;
    }

    location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
    expires 1y;
    access_log off;
    add_header Cache-Control "public";
    }

    location ~* \.(?:css|js)$ {
    try_files $uri =404;
    expires 1y;
    access_log off;
    add_header Cache-Control "public";
    }

    location ~ ^.+\..+$ {
    try_files $uri =404;
    }

    error_page 500 502 503 504 /50x.html;

    location = /50x.html {
    root /usr/share/nginx/html;
    }
    }
    }
    Dockerfile:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    FROM nginx:1.17.9-alpine

    ENV TZ=Asia/Shanghai

    WORKDIR /usr/share/nginx/html

    COPY ./config/nginx.conf /etc/nginx/nginx.conf

    COPY ./dist /usr/share/nginx/html

    EXPOSE 80
  3. 将对应的文件打包成镜像,然后部署到宿主机上,我使用的是coding的持续集成,对应的jenkinsfile如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    pipeline {
    agent any
    stages {
    stage('检出') {
    steps {
    checkout([$class: 'GitSCM', branches: [[name: env.GIT_BUILD_REF]],
    userRemoteConfigs: [[url: env.GIT_REPO_URL, credentialsId: env.CREDENTIALS_ID]]])
    }
    }
    stage('构建') {
    steps {
    echo '构建中...'
    sh 'npm install'
    sh 'npm run deploy'
    sh 'docker build -t ${registry}/${projectName}:${GIT_COMMIT} .'
    sh "docker login --username='${username}' --password='${password}' '${registry}'"
    sh 'docker push ${registry}/${projectName}:${GIT_COMMIT}'
    echo '构建完成.'
    }
    }

    stage("部署到远端服务") {
    steps {
    script {
    def remoteConfig = [:]
    remoteConfig.name = "my-remote-server"
    remoteConfig.host = "10.0.0.1"
    remoteConfig.port = 22
    remoteConfig.allowAnyHosts = true

    withCredentials([
    sshUserPrivateKey(
    credentialsId: "${remote_cred}",
    keyFileVariable: 'id_rsa'
    ),
    ]) {
    // SSH 登陆用户名
    remoteConfig.user = "root"
    // SSH 私钥文件地址
    remoteConfig.identityFile = id_rsa

    // 请确保远端环境中有 Docker 环境
    sshCommand(
    remote: remoteConfig,
    command: "docker login -u ${username} -p ${password} ${registry}",
    sudo: true,
    )

    sshCommand(
    remote: remoteConfig,
    command: "docker rm -f ${containerName} | true",
    sudo: true,
    )

    // DOCKER_IMAGE_VERSION 中涉及到 GIT_LOCAL_BRANCH / GIT_TAG / GIT_COMMIT 的环境变量的使用
    // 需要在本地完成拼接后,再传入到远端服务器中使用
    DOCKER_IMAGE_URL = sh(
    script: "echo ${registry}/${projectName}:${GIT_COMMIT}",
    returnStdout: true
    )

    sshCommand(
    remote: remoteConfig,
    command: "docker run -d -p 2111:80 --name ${containerName} ${DOCKER_IMAGE_URL}",
    sudo: true,
    )

    echo "部署成功,请到 https://1410.xxxx.com 预览效果"
    }
    }
    }
    }
    stage('上传CDN') {
    steps {
    echo '构建中...'
    sh 'node index.js'
    echo '上传完成'
    }
    }

    }
    environment {
    registry = ''
    username = ''
    password = ''
    projectName = '1410'
    containerName = '1410'
    remote_cred = ''
    }
    }
  4. 为防止docker镜像启动不成功,导致原有的网站不可访问,所以将对应的dist中的文件上传到CDN中作为最后一步,这样可以保证服务启动后,访问的是最新的站点,将dist文件上传到cos上我使用的是我前期写的一个开源项目alidaodao-cos-uploader,具体的步骤可以访问项目后进行配置。

到此整个自动化部署的过程也结束了,可以看看对应的运行步骤截图:
coding持续集成

背景

最近在调研一款能管理我的tencent-cos的在线管理端软件,然后调研了很多软件,之前试用过nextcloud发现一般,然后看中了minio,看评测说性能比较好,并且支持S3协议。

在实际使用过程中是使用的docker部署,具体命令如下,对应文档可以点击minio-gateway

1
2
3
4
docker run -p 9000:9000 --name minio-s3 \
-e "MINIO_ACCESS_KEY=access_key" \
-e "MINIO_SECRET_KEY=secret_key" \
minio/minio gateway s3 https://cos.ap-beijing.myqcloud.com

然而在我将信息填入以后,发现始终无法创建,提示:ERROR Unable to initialize gateway backend: Could not parse the specified URI.
然而通过中心搜索并不能寻找到结果,然后使用全英文搜索就找到问题所在了,原因是:tencent-cos的bucket命名是以: bucket名+个人的账号数字为命名方式的,和minio的默认创建桶的方式不一样,所以始终提示该错误。

改造过程

既然问题已经找到,剩下的问题就解决问题了,解决问题一般有两种方案,一般是解决创造问题的人,一种是直接解决,然而通过腾讯云的工单并不能解决问题,直接说是第三方的原因导致的,无法进行修改。
直接解决问题也很简单:

  • 先找到minio的github仓库,然后fork到自己的仓库中,
  • 按文件夹查找cmd->gateway->s3->gateway-s3.go文件
  • 然后找到randString这个方法
  • 修改最后的返回值为:return prefix + string(b[0:30-len(prefix)]) + "-123"
  • 此时问题解决。

背景

春运已至,又到了回家团圆的时候了,现在春运抢票越来越难,原因是很多人开始使用技术来抢票,在高配置高带宽的服务器上抢票几率要稍微高一点,希望我这篇文章能帮助正在阅读的你抢到回家的火车票。

起因

前段时间在鼓捣数据表的数据上线,主要流程是将线下的数据同步到线上去,线上的部分需要需要和线下保持一直,并且每一次操作都需要自动化将表进行备份, 这个过程主要是靠自己进行代码同步,因为规则比较自定义,所以没有使用一些现有的数据同步。

主要流程如下:

1
2
3
4
5
6
7
8
9
- #备份NX的SCHEMA中的表并查询特定数据进行备用
NX-SCHEMA: 备份NX-TABLE ==> NX-TABLE_COPY ==> SELECT * FROM NX-TABLE_COPY WHERE ID =xx

- #备份JAPAB的SCHEMA中的表并且将上一步的数据写入到备份表中
JAPAN-SCHEMA: BEFEN JAPAN-TABLE ==> JAPAN-TABLE_COPY ==> INSERT INTO JAPAN-TABLE_COPY VALUE (xxx)

- #将JAPAN的SCHEMA中的原表和备份表进行重命名,将备份表的表名变成源表名,完成数据上线
JAPAN-SCHEMA: JAPAN-TABLE ==> RENAME JAPAN-TABL XXX ==> RENAME JAPAN-TABLE_COPY TO JAPAN-TABLE

在这个过程中,我们最主要的一步是在同一个schema下进行将原表进行备份,创建一个对应的不同表,后续所有的操作都改这个表中操作,在这个过程中也是出现了一些问题,后续经过实践后解决了相关问题,特此记录。

经过

我们在备份源Schema中的table时,直接采取了简单粗暴的SQL语句,如下:create table table_name_copy as select * from table_name,该SQL实现了创建table_name_copy并且将table_name中的数据也插入到对应的新表中,
看似满足了我们的需求:朴素的备份源表,不进行任何操作。

然而,简单的事情总是不会那么简单,在我们进行数据比对时,发现数据没啥问题,但是在校验表的DDL时,发现麻烦稍微有点大,此次备份基本没用,因为此时我们的表的主键、索引等等都丢失了,然后我们再查询相关的文档,发现弊端还挺多。

然后在此上进行了改进,先根据ddl创建表的结构,然后再讲数据导入进来,这样就避免了锁、和索引等问题。SQL如下:

1
2
3
4
5
6
7
8
9
10
第一步:创建表结构

方法一: 按照老表的结构创建新表
create table new_table like old_table;
方法二: 此种方法是先获取ddl,然后再修改表名再次执行DDL,进行表结构创建
SHOW CREATE TABLE old_table;

第二步:同步原表数据
INSERT INTO new_table SELECT * FROM old_table;

备份表的结构和数据都还是比较简单,但是这个只适用于少量数据的备份,大量数据的备份暂时还没有进行实践,我们大量的数据备份一般使用CSV或者ETL进行同步。

本文翻译自 StreamNative 博客。博客原作者:Ioannis Polyzos,StreamNative 解决方案工程师。原文链接:https://streamnative.io/blog/engineering/2021-11-10-streaming-data-pipelines-with-pulsar-io/

翻译背景

前言

朋友买了一台M1的MBP,但是又需要安装财务软件(财务软件需要安装在windows系统上),M1目前还不支持原生安装WIN10,所以采用虚拟安装的方法来安装对应的WIN10。

前言

我们的数据计算式基于clickhouse的,由于接触clickhouse不久,看官网介绍语法和mysql是类似的,就放心大胆的使用mysql的大量语法,然后遇到了一个很奇怪的问题,也是这个奇怪的问题让我对列式数据库有了更深入的了解。

前言

今年开始接触并且实践到Golang,近期自己写了一个相册的服务,是基于前后端分离的模式,由Go提供rest给web页面使用。在项目前期是直接使用的打包完成的二进制文件执行,在管理方面存在一些不方便的地方,所以周末抽时间将其容器化,实现自动化的部署方案,主要就是采用coding的devops流程,容器化使用的还是docker容器,使用的是alpine的镜像,在这个过程中遇到一些问题,下面会详细讲出,以此记录。

前言

上一篇文章写了怎么部署standard notes的自建笔记本,在使用过程中发现还需要很多的插件来配合更好的使用standard notes,来做数据备份以及更好的编辑文档。
standard notes的插件是可插拔的,简而言之就是将静态页面加载到页面中,来使用其中的功能,达到增强的目的。

前言

很久很久之前就一直在找一些好用的并且开源的note app,尝试过trilium、蚂蚁笔记、notion、语雀 都没有找到我心仪的那一款,要么是数据是存储在境外,一旦国家的墙更厚了,就尬了,另外一些就是数据很分散,备份出来的数据无法很顺利的导入到其他的app中。
直到有一天我发现了Standard notes。