2015年7月

Microservices architectures pros and cons 微服务架构的优缺点

这里全是引用或者从这篇文章引发的: Microservices - Not A Free Lunch!

pros:

    每个单独的服务都是以某个业务模块为中心, 不用关心其他服务, 开发简单;
    sale out, 伸缩性, HA
    rollback only involve single app
    每个服务都可以根据自己业务特性, 采用不同的内部架构, 工具, 开发语言;
    每个服务可以根据load 情况, 增加减少cluster内机器的数量;
    系统中的服务都是松耦合的, 便于开发, 维护;
    每个服务的开发团队都不至于太大, 团队之间相互独立;
    现在很多的开源的工具支持这种开发模式, 便于上手;

cons:

    原来你测试部署一个app就好了, 那么现在按照模块, 根据微服务架构拆分成了多个app, 相互提供服务, 那么你逻辑上就要有多个team, 多个app, 原来测试, 部署一个app就好了, 现在要部署测试多个app, 要有更多的开发, 测试, 生产环境;
    原来一个app 内部之间直接调用, 现在要通过网络, 要有服务注册, 发现机制; 网络之间的调用, 就有了failover;
    多个prodution cluster 在线上, 那么就需要等多的mnoitor, 报警机制, 以及分布式的log 机制;
    Substantial DevOps Skills Required
    定义接口, 单机应用的接口可以随时改变, 然后修改, 微服务的分布式架构就要求协调多个team, 按计划发布; 并且接口最好在一段时间内不要修改;
    Once we have distributed a system, we have to consider a whole host of concerns that we didn't before. Network latency, fault tolerance, message serialisation, unreliable networks, asynchronicity, versioning, varying loads within our application tiers etc.
    Asynchronicity Is Difficult!
    Testability Challenges

Using Docker [读书笔记] 3 docker 命令

docker run

--restart 参数 可以是 no | always | on-failure:10, 所以可以设置是否自动启动
--rm 自动删除 container 当container 退出的时候;
-e, --env 设置环境变量
--env-file 设置环境变量名字
-h, --hostname
--name NAME
-v, --volume
--volumes-from
--expose
--link
-p, --publish 发布一个端口, 如果不给定, 就随机设置一个未用的
-P, --publish-all
--add-host Adds the given IP and hostname mapping to /etc/hosts in the container.
--dns 定制 dns server
--mac-address 设置 MAC 地址
--net 设置网络模式 bridge | none | container | host
-c, --cpu-shares CPU 使用量
-cap-add / --cap-drop 增加 或 去除 linux 某个功能
--cpuset 可以使用那个CPU
--device 设置 container 可以访问那个那个硬件设备 如 磁盘, 打印机, 声卡
-m, --memory 内存使用量 设置
--entrypoint override ENTRYPOINT
-u, --user override USER
-w, --workdir

管理 Container

    docker attach [OPTIONS] CONTAINER   
    docker create  从image 创建一个container, 但是不run, 后续可以使用 docker start 去run
    docker cp  从container copy 文件或路径到 host
    docker exec  run 一个在container 的命令
    docker kill  杀死 container 的main process (PID : 1)
    docker pause  / docker unpause  冻结/解冻 container 的所有进程
    docker rename  重命名 一个 container
    docker restart  = docker stop; docker start
    docker rm   remove one or more containers. -f 选项强制remove 正在运行的, -v 去除相关volume

Container 相关命令

    docker diff  对比从image 到现在container 为止 container文件系统做的变化
    docker events   打印实时事件信息
    docker inspect   查看 image 或 container 的相关信息
    docker logs    查看 container 的STDOUT, STDERR 相关信息
    docker port    查看 container 对应的端口 mapping 信息
    docker ps  查看在run 或者 停止的 container 的 high-level 相关信息, -q 只返回container id, 一般用着其他后续命令
    docker top 类似 top, 不过只和这个container 相关的, 也就是container 内部的进程

Image 相关命令

    docker build  从Dockerfile 创建 image 
    docker commit  从 container 创建 image. 从一个正在运行的container 创建 image, 可能导致它暂停, 有参数 --pause=false 可以设置
    docker history  Outputs information on each of the layers in an image.
    docker images  查看本地 image 信息
    docker rmi   删除本地 某 image
    docker tag   给image 打tag

Registry 相关命令 .dockercfg in your home directory

    docker login 
    docker logout
    docker push
    docker search

Using Docker [读书笔记] 2

Dockerfile 中的 EXEC VS SHELL 形式
Several instructions (RUN, CMD and ENTRYPOINT), take both a shell format and an exec format. The exec form takes a JSON array (e.g. ["executable", "param1", "param2"]) which assumes the first item is the name of an executable which is then executed with the remaining items as parameters. The shell format is a freeform string which will be interpreted by passing to /bin/sh -c. Use the exec form to avoid the shell munging strings or in cases where the image doesn’t have /bin/sh.

FROM base image
ADD 从远程或本地Context copy到image, 如果是压缩文件, 会自动解压. 最好不要用ADD, 用COPY 或 RUN wget/curl;
CMD 当Container启动完, 执行的命令. 后边的CMD 会覆盖前面的CMD;
COPY 有EXEC 和 SHELL 格式, EXEC 的json 格式支持路径中有空格, 支持Wildcard, 但不支持到Context的上层目录;
ENV 设置环境变量
RUN run 给定的脚本, 并commit 其结果
USER 设置用户, 用以给后续的CMD, RUN, COPY, ENTRYPOINT 等用; host 和Container 之间的userId是一样的, user name 可能不一样.
EXPOSE 标明要监听的端口, 用以告诉docker server.
ENTRYPOINT Container 启动后默认执行的脚本, 可能被CMD 或 docker run的命令覆盖.
MAINTAINER 维护者信息
ONBUILD 如果本image 被用作base image, 那么后续build 时候要执行的脚本
VOLUME 设置要挂载的文件
WORKDIR 设置工作路径, 用来给RUN, CMD, ENTRYPOINT, ADD or COPY 等命令用

Docker links are the simplest way to allow containers on the same host to talk to each other. Using Docker links will also add the alias and the link container ID to /etc/hosts on the master container, allowing the link container to be addressed by name from the master container.By default, containers will be able to talk to each other whether not they have been explicitly linked. Perhaps most significantly they are static — links aren’t updated if the link container goes down or is replaced. Also, the link container must be started before the master container, meaning you can’t have bidirectional links.

Volumes are directories[18] that are not part of the container’s UFS, they are just normal directories on the host that are bind mounted into the container.

通过Dockerfile的 VOLUME命令, 不能指定host的文件路径, 所以都是动态指定的, 所以可以通过 docker inspect -f {{.Volumes}} ${containerId} 查看
通过docker run -v hostDir:containerDir 指定路径.

通过 --volumes-from CONTAINER shared volume 即使原container 没有在run, 照样可以使用这个volume.

使用Html5 Canvas 画芝麻信用的截屏

最近经常有人在微信朋友圈晒芝麻信用的分数, 虽然我的分数没有达到欧洲某小国申根的要求, 不过也不低. 今天台风"灿鸿" 袭来, 外边大风又大雨, 又是周末, 于是就想用 html5 的canvas 做一个假的芝麻信用截图.
下面是一个效果图:
latest.png

如果你想查看源代码, 或者修改, 看jsfiddle上的share
https://jsfiddle.net/manecocomph/edkg8z5w/1/

源代码:
html 源代码:

<canvas id="fakeImg"></canvas>

Javascript 源代码:

function drawTextAlongArc(context, str, centerX, centerY, radius, angle) {
    var len = str.length, s;
    context.save();
    context.translate(centerX, centerY);
    context.rotate(-1 * angle / 2);
    context.rotate(-1 * (angle / len) / 2);
    for (var n = 0; n < len; n++) {
        context.rotate(angle / len);
        context.save();
        context.translate(0, -1 * radius);
        s = str[n];
        context.fillText(s, -14, 0);
        context.restore();
    }
    context.restore();
}

// init score
var score = 849;
var screenWidth = 640;
var screenHeight = 1080;
var generalWordColor = 'gray';

var halfScreenWidth = screenWidth / 2;

// calculate angle & desc
var scoreDesc = "信用极好";
var angle = 1.8;
if (score <= 350) {
    angle = 0.9;
    scoreDesc = "信用极差";
} else if (score > 350 && score <= 550) {
    angle = 0.9 + ((score - 350) / 200) * ((2.1 - 0.9) / 5);
    scoreDesc = "信用较差";
} else if (score > 550 && score <= 700) {
    angle = 0.9 + (2.1 - 0.9) / 5 + ((score - 550) / 150) * ((2.1 - 0.9) / 5 * 3);
    if (score > 550 && score <= 600) {
        scoreDesc = "信用中等";
    } else if (score > 600 && score <= 650) {
        scoreDesc = "信用良好";
    } else {
        scoreDesc = "信用优秀";
    }
} else if (score > 700 && score <= 950) {
    angle = 0.9 + (2.1 - 0.9) / 5 * 4 + ((score - 700) / 250) * ((2.1 - 0.9) / 5);
    scoreDesc = "信用极好";
} else {
    angle = 2.1;
    scoreDesc = "信用爆棚";
}

// get canvas and set width & height
var canvas = document.getElementById("fakeImg");
canvas.setAttribute("width", screenWidth);
canvas.setAttribute("height", screenHeight);
var ctx = canvas.getContext("2d");

///// 开始画顶端 ////////
//header
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, screenWidth, 90);
//手机顶端
ctx.fillStyle = '#FFFFFF';
ctx.font = '10px Arial';
ctx.fillText("中国移动  4G", 5, 12);
ctx.fillText("13:59", halfScreenWidth - 22, 12);
ctx.fillRect(screenWidth - 50, 3, 10, 9);
ctx.strokeStyle = '#FFFFFF';
ctx.strokeRect(screenWidth - 50, 3, 30, 9);
ctx.font = '4px Arial';
ctx.fillText("▍", screenWidth - 20, 9);
//标题顶端
ctx.font = '24px Arial';
ctx.fillText("ㄑ 财富", 15, 60);
ctx.font = '28px Arial';
ctx.fillText("芝麻信用", halfScreenWidth - 66, 60);
ctx.strokeStyle = '#FFFFFF';
ctx.beginPath();
ctx.arc(screenWidth - 50, 50, 13, Math.PI * 2, 0, true);
ctx.stroke();
ctx.font = '20px Arial';
ctx.fillText("?", screenWidth - 55, 57);

///// 搜索部分
ctx.fillStyle = '#E4EDEC';
ctx.fillRect(0, 90, screenWidth, 60);
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(60, 95, screenWidth - 120, 50);
ctx.font = '20px Arial';
ctx.fillStyle = 'gray';
ctx.fillText("看看Ta的芝麻分", 228, 130);
//search
ctx.beginPath();
ctx.arc(100, 122, 11, Math.PI * 2, 0, true);
ctx.strokeStyle = 'gray';
ctx.moveTo(108, 130);
ctx.lineTo(116, 137);
ctx.stroke();
//情怀部分
ctx.font = '26px Arial';
ctx.fillStyle = 'gray';
ctx.fillText("一  滴滴珍贵 重在参与  一", halfScreenWidth - 157, 203);

//////// 开始画中间的数据 //////////////
// init liner gradient, prepare for rainbow diagram
var grd = ctx.createLinearGradient(screenWidth / 9, screenHeight / 2, screenWidth / 9 * 8, screenHeight / 2);
grd.addColorStop(0, 'red');
grd.addColorStop(0.25, '#F99501');
grd.addColorStop(0.35, '#C1D500');
grd.addColorStop(0.55, '#AAE400');
grd.addColorStop(1, '#06D780');

// set circle center x,y
var circleCenterX = halfScreenWidth;
var circleCenterY = screenHeight / 2;

ctx.lineWidth = 25;
ctx.strokeStyle = grd;
// circle center : halfScreenWidth, circleCenterY 扇形部分
for (var i = 0; i < 45; i++) {
    ctx.beginPath();
    if (8 == i % 9) {
        ctx.arc(halfScreenWidth, circleCenterY, screenWidth / 8 * 3, Math.PI * ((i + 1) * 0.02666 + 0.897), Math.PI * (0.9 + i * 0.02666), true);
    } else {
        ctx.arc(halfScreenWidth, circleCenterY, screenWidth / 8 * 3, Math.PI * ((i + 1) * 0.02666 + 0.899), Math.PI * (0.9 + i * 0.02666), true);
    }
    ctx.stroke();
}

// draw credit level
ctx.fillStyle = generalWordColor;
ctx.font = '16px Arial';
ctx.lineWidth = 1;
var str = ['350', '较差', '550', '中等', '600', '良好', '650', '优秀', '700', '较好', '950'];
drawTextAlongArc(ctx, str, halfScreenWidth, circleCenterY, screenWidth / 8 * 3 - 35, 3.95);

// 画内圈虚线
ctx.lineWidth = 1;
ctx.strokeStyle = 'gray';
for (var i = 0; i < 120; i = i + 2) {
    ctx.beginPath();
    ctx.arc(halfScreenWidth, circleCenterY, screenWidth / 4, Math.PI * (0.9 + (i + 1) * 0.01), Math.PI * (0.9 + i * 0.01), true);
    ctx.stroke();
}

// drow outer circle 
ctx.lineWidth = 3;
if (angle > 0.92) {
    ctx.beginPath();
    ctx.arc(halfScreenWidth, circleCenterY, screenWidth / 16 * 5, Math.PI * (angle - 0.02), Math.PI * 0.9, true);
    ctx.strokeStyle = '#06D780';
    ctx.stroke();
}

if (angle < 2.08) {
    ctx.beginPath();
    ctx.arc(halfScreenWidth, circleCenterY, screenWidth / 16 * 5, Math.PI * 2.1, Math.PI * (angle + 0.02), true);
    ctx.strokeStyle = 'gray';
    ctx.stroke();
}

// 开始画指针 第一步 指针三角 
var anchorX1 = halfScreenWidth + screenWidth / 16 * 5 * Math.cos((angle - 0.02) * Math.PI);
var anchorY1 = circleCenterY + screenWidth / 16 * 5 * Math.sin((angle - 0.02) * Math.PI);
var anchorX2 = halfScreenWidth + screenWidth / 16 * 5 * Math.cos((angle + 0.02) * Math.PI);
var anchorY2 = circleCenterY + screenWidth / 16 * 5 * Math.sin((angle + 0.02) * Math.PI);
var anchorX3 = halfScreenWidth + (screenWidth / 16 * 5 - 36) * Math.cos(angle * Math.PI);
var anchorY3 = circleCenterY + (screenWidth / 16 * 5 - 36) * Math.sin(angle * Math.PI);
ctx.fillStyle = '#06D780';
ctx.beginPath();
ctx.moveTo(anchorX1, anchorY1);
ctx.lineTo(anchorX3, anchorY3);
ctx.lineTo(anchorX2, anchorY2);
ctx.fill();
ctx.closePath();

// 指针后部的2个圆圈的中心
var anchorX = halfScreenWidth + screenWidth / 16 * 5 * Math.cos(angle * Math.PI);
var anchorY = circleCenterY + screenWidth / 16 * 5 * Math.sin(angle * Math.PI);
// 指针后部 外环圆
ctx.strokeStyle = '#06D780';
ctx.beginPath();
ctx.arc(anchorX, anchorY, 12, 0, 2 * Math.PI, false);
ctx.stroke();
// 指针后部 中间白色圆
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = 10;
ctx.beginPath();
ctx.arc(anchorX, anchorY, 5, 0, 2 * Math.PI, false);
ctx.stroke();
// 指针后部 最内部绿色圆
ctx.fillStyle = '#06D780';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(anchorX, anchorY, 5, 0, 2 * Math.PI, false);
ctx.fill();

// 内部文字部分
ctx.fillStyle = generalWordColor;
ctx.fillText("BETA", halfScreenWidth - 24, (circleCenterY - screenWidth / 4 + 62));
ctx.font = '94px Arial';
ctx.fillStyle = '#08BC8C';
ctx.fillText(score, halfScreenWidth - 84, (circleCenterY - screenWidth / 4 + 162));
ctx.font = '44px Arial';
ctx.fillText(scoreDesc, halfScreenWidth - 89, (circleCenterY - screenWidth / 4 + 212));
ctx.fillStyle = 'gray';
ctx.font = '14px Arial';
ctx.fillText("评估时间: 2015.07.11", halfScreenWidth - 74, (circleCenterY - screenWidth / 4 + 238));
ctx.strokeStyle = '#08BC8C';
ctx.strokeRect(halfScreenWidth - 85, (circleCenterY - screenWidth / 4 + 342), 180, 60);
ctx.font = '30px Arial';
ctx.fillStyle = '#08BC8C';
ctx.fillText("嘚瑟一下", halfScreenWidth - 50, (circleCenterY - screenWidth / 4 + 386));
ctx.font = '16px Arial';
ctx.fillStyle = 'gray';
ctx.fillText("高粱信用是合法独立的信用评估机构及信用采集机构", halfScreenWidth - 161, (circleCenterY - screenWidth / 4 + 432));

/////////// 开始画底部
//footer 的 2个点
ctx.fillStyle = '#06D780';
ctx.beginPath();
ctx.arc(halfScreenWidth - 35, screenHeight - 100, 8, 0, 2 * Math.PI, false);
ctx.fill();
ctx.fillStyle = 'gray';
ctx.beginPath();
ctx.arc(halfScreenWidth + 35, screenHeight - 100, 8, 0, 2 * Math.PI, false);

// 低端标题
ctx.fill();
ctx.fillStyle = '#EFF6EF';
ctx.fillRect(0, screenHeight - 80, screenWidth, 80);
ctx.font = '34px Arial';
ctx.fillStyle = '#27876B';
ctx.fillText("信用猜猜", 80, screenHeight - 25);
ctx.fillText("信用生活", halfScreenWidth + 80, screenHeight - 25);
ctx.strokeStyle = '#DBE4DF';
ctx.beginPath();
ctx.moveTo(0, screenHeight - 80);
ctx.lineTo(screenWidth, screenHeight - 80);
ctx.moveTo(halfScreenWidth, screenHeight - 80);
ctx.lineTo(halfScreenWidth, screenHeight);
ctx.stroke();

Using Docker [读书笔记] 1

Using Docker [读书笔记]

Containers are a lightweight and portable store for an application and its dependencies. 序的开头这么一句话, lightweight应该是较VM而言, 这里的container确实包含了你的应用及其依赖;
containerization: 集装箱化;
Containers share resources with the host OS;

Container 里面ps 看进程
psInContainer.png
Host OS 查看 docker 进程
psOutContainer.png

如果选择让container知道外部的线程, 那么container 内部其实是可以看到的
inContainerWithPidOption.png

The purpose of a VM is to fully emulate a foreign environment, whilst the purpose of a container is to make applications portable and self-contained;

Containers are an old concept. For decades, UNIX systems have had the chroot command which provides a simple form of filesystem isolation. FreeBSD has had the jail utility since 1998, which extended chroot sandboxing to processes. Solaris Zones offered a comparatively complete containerization technology around 2001, but was limited to the Solaris OS. Also in 2001, Parrallels Inc (then SWsoft) released the commercial Virtuozzo container technology for Linux, and later open sourced the core technology as OpenVZ in 20051. Following on from this, Google started the development of CGroups for the Linux kernel and began moving their infrastructure to containers. The LXC project started in 2008 and brought together CGroups, kernel namespaces and chroot technology (amongst others) to provide a complete containerization solution. Finally, in 2013, Docker brought the final pieces to the containerization puzzle and the technology began to enter the mainstream.

Docker took the existing Linux container technology then wrapped it and extended it in various ways — primarily portable images and a user-friendly interface — to create a complete solution for the creation and distribution of containers. The Docker platform has two distinct components; the Docker Engine, which is responsible for creating and running containers and the Docker Hub, a cloud service for distributing containers.

The Docker Engine provides a very fast and convenient interface for running containers. Before this, running a container using a technology such as LXC required significant specialist knowledge and manual work. The Docker Hub provides an enormous number of public container images for download, allowing users to quickly get started and avoid duplicating work already done by others.

Early versions of Docker were little more than a wrapper around LXC paired with a Union Filesystem when start open-source;

Docker uses a Union File System (UFS) for containers, which allows multiple file systems to be mounted in a hierarchy and appear as a single file system. The file system from the image has been mounted as a read-only layer and any changes to the running container are made to a read-write layer mounted on top of this.

删除退出的container
$ docker rm $(docker ps -aq -f status=exited)