Docker 是一个基于容器的应用开发、部署和运行平台,它为开发者和系统管理员们提供了一种新式的应用部署方式,具有灵活(最复杂的应用都能容器化)、轻量(容器共享一个服务器内核)、可替换的(可以在容器运行过程中更新服务器)、可移植的(本地、云上皆可)、可伸缩的(可以轻松地进行复制)、可栈化(指的是可以将多个服务部署在一起,比如用 docker-compose)的特性。 Docker is a platform for developers and sysadmins to develop, deploy, and run applications with containers. The use of Linux containers to deploy applications is called containerization. Containers are not new, but their use for easily deploying applications is. Containerization is increasingly popular because containers are:
Flexible: Even the most complex applications can be containerized.
Lightweight: Containers leverage and share the host kernel.
Interchangeable: You can deploy updates and upgrades on-the-fly.
Portable: You can build locally, deploy to the cloud, and run anywhere.
Scalable: You can increase and automatically distribute container replicas.
Stackable: You can stack services vertically and on-the-fly.
Docker 优势
容器技术相比虚拟机,主要优势在于性能上,其性能优势可以说达到了一个量级的差距。根据 Boden Russell 在 OpenStack 上做的一次基准测试报告,一个 KVM 实例的平均内存消耗有 292MB,而一个 docker 实例的平均内存消耗在 49MB 左右。在 CPU overhead 和启动时间方面,docker 基本都比 KVM 有一个量级的优势。 目前,一个 AWS 上的 micro 实例,每小时的按需使用成本大约在一美分多一些。如果用 docker 来提供实例,那么每小时的按需使用成本很可能会做到 0.1 美分。这一点对于云经济至关重要。正如经济学家 William Stanley Jevons 的理论所呈现的,随着商品的价格越便宜,人们使用它们的场景和频率会越来越多。
Runtime performance at near bare metal speeds (typically 97+ percent or bare metal – a few ticks shaven off for bean counters).
Management operations (boot, stop, start, reboot, etc.) in seconds or milliseconds.
Agile
VM-like agility – it’s still “virtualization”.
Seamlessly move between virtual and bare metal environments permitting new development workflows which reduce costs (e.g. develop on VMs and move to bare metal in the “click of a button” for production).
Flexible
Containerize a “system” (OS less the kernel).
Containerize “application(s)”.
Lightweight
Just enough Operating System (JeOS); include only what you need reducing image and container bloat.
Minimal per container penalty which equates to greater density and hence greater returns on existing assets – imagine packing 100s or 1000s of containers on a single host node.
Inexpensive
Open source – free – lower TCO.
Supported with out-of-the-box modern Linux kernels.
Ecosystem
Growing in popularity – just checkout the google trends for docker or LXC.
Vibrant community and numerous 3rd party applications (1000s of prebuilt images on docker index and 100s of open source apps on github or other public sources).
Cloudy
Various Cloud management frameworks provide support for creating and managing Linux Containers – including OpenStack my personal favorite.
A container is launched by running an image. An image is an executable package that includes everything needed to run an application–the code, a runtime, libraries, environment variables, and configuration files. A container is a runtime instance of an image–what the image becomes in memory when executed (that is, an image with state, or a user process). You can see a list of your running containers with the command, docker ps, just as you would in Linux. 一个镜像是:
传统的部署云服务的方式是通过虚拟机完成的,虚拟机会在宿主机上运行一个完整的操作系统、通过hypervisor来间接使用宿主机的硬件资源,实际上这远远超出了应用运行所必须的资源。而容器正相反,它在操作系统中作为进程运行,与所有其他容器共享同一内核、占用相同容量的内存空间,相对来说,会更加轻量。 A container runs natively on Linux and shares the kernel of the host machine with other containers. It runs a discrete process, taking no more memory than any other executable, making it lightweight. By contrast, a virtual machine (VM) runs a full-blown “guest” operating system with virtualaccess to host resources through a hypervisor. In general, VMs provide an environment with more resources than most applications need. 下图是Docker(容器)和传统虚拟机之间运行架构的示意图。 Container stack example Virtual machine stack example In reality virtualization and Docker can and are used together in modern dev-ops. Most VPS providers are running bare-metal full virtualization technologies like Xen and Docker usually runs on top of a virtualized Ubuntu instance.
The docker daemon binds to a Unix socket instead of a TCP port. By default that Unix socket is owned by the user root and other users can access it with sudo. For this reason, docker daemon always runs as the root user. To avoid having to use sudo when you use the docker command, create a Unix group called docker and add users to it. When the docker daemon starts, it makes the ownership of the Unix socket read/writable by the docker group.
/* Prototype for the glibc wrapper function */ #define _GNU_SOURCE #include <sched.h> int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... /* pid_t *ptid, void *newtls, pid_t *ctid */ ); /* For the prototype of the raw system call, see NOTES */
clone常用于实现多线程,因为子进程和父进程可以共享内存。 不同于fork创建的子进程会从调用的位置开始执行,clone创建的子进程会执行实参传入的fn(arg),并将实参中的arg传入。 当fn(arg)返回后子进程页会终止,返回值即为子进程的exit code,当然子进程在遇到显式的exit调用或终止信号也会立刻退出。 子进程与父进程共享内存,它们不能(也不应该)使用同一个栈,因此必须使用child_stack参数指定子进程使用的栈所在的内存空间。栈是从上向下生长的,因此最好指定最顶层的一个地址。 flags的低位包含子进程退出了发送给父进程的信号,If this signal is specified as anything other than SIGCHLD, then the parent process must specify the __WALL or __WCLONE options when waiting for the child with wait(2). If no signal is specified, then the parent process is not signaled when the child terminates. flags 还可以指定子进程和父进程间可以共享的内容,具体内容见man clone。
VETH: Typically used when you are trying to connect two entities which would want to “get hold of” (for lack of better phrase) an interface to forward/receive frames. These entities could be containers/bridges/ovs-switch etc. Say you want to connect a docker/lxc container to OVS. You can create a veth pair and push the first interface to the docker/lxc (say, as a phys interface) and push the other interface to OVS. You cannot do this with TAP.
veth设备特点
veth和其它的网络设备都一样,一端连接的是内核协议栈
veth设备是成对出现的,另一端两个设备彼此相连
一个设备收到协议栈的数据发送请求后,会将数据发送到另一个设备上去
常用命令
1 2
# 创建veth ip link add name veth0 type veth0 peer name veth1
private Result doInvoke(List<Invoker<T>> invokers, final List<Invoker<T>> invoked, Holder<RpcException> lastException, final Set<String> providers, final Invocation invocation, final LoadBalance loadbalance, final int totalRetries, int retries, Holder<Invoker<T>> lastInvoked) throws RpcException { if (retries < totalRetries) { checkWheatherDestoried(); invokers = list(invocation); checkInvokers(invokers, invocation); }
public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) { this.virtualInvokers = new TreeMap<Long, Invoker<T>>(); this.identityHashCode = identityHashCode; URL url = invokers.get(0).getUrl();
String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0")); argumentIndex = new int[index.length]; for (int i = 0; i < index.length; i++) { argumentIndex[i] = Integer.parseInt(index[i]); }
int replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160); for (Invoker<T> invoker : invokers) { String address = invoker.getUrl().getAddress(); // 多复制几个,更均匀,避免所有请求都被hash到同一个Invoker for (int i = 0; i < replicaNumber / 4; i++) { byte[] digest = md5(address + i); for (int h = 0; h < 4; h++) { long m = hash(digest, h); // 放入圆环上 virtualInvokers.put(m, invoker); } } } }
public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( new String[]{"classpath:consumer.xml"});
final UserServiceBo demoService = (UserServiceBo) context.getBean("userService");
Dubbo 是一个分布式服务框架,是阿里巴巴 SOA 服务化治理方案的核心框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。简而言之,Dubbo 是个远程服务调用的分布式框架(告别 Web Service 模式中的 WSdl,以服务提供者与消费者的方式在 dubbo 上注册)。
通过版本号,也可以实现消费者和提供者服务端直接连接,因为发起调用默认使用随机调用端负载均衡模式,当有多台提供者的时候,会随机选取,通常联调阶段都会调用指定服务进行联调,直连一般用在调试,开发阶段,只需要消费者和提供者 version 相同即可。
灰度发布
有三台服务器 A、B、C 要上线,现在三台服务器都是旧版本代码,那首先从 Ngnix 负载均衡列表里移除 A 服务器的配置,切断对 A 的访问,然后在 A 服务器不受新的代码,重新把 A 配置进 Ngnix 负载均衡列表。如果在线使用没有问题,则继续升级 B、C 服务器,否则回滚,恢复旧版本代码,这是针对三端(PC 端,微信端,移动端)跟网关系统的。 如果是针对子系统,譬如用户系统、订单系统等,可以通过分组 group 来实现子系统的灰度发布。服务提供者有两组,One、Two,将新版本代码 group 改为 Two,旧版本 group 还是 One,将新版本的消费者 group 改为 Two,这时请求定位到新的消费者再调用新的提供者,而且旧的消费者还是请求旧的提供者,如果线上没有问题,那就把提供者 group 为 One 的组改为 Two,并部署新代码,旧的消费者也改成 Two 并部署新代码如果有问题,那消费端和提供端都回滚到旧版本。
异步调用
Dubbo 默认情况下是同步调用的,就是调用后立刻返回,但如果消费端调用服务端创建文件并转化成 PDF 格式的文件这种在 IO 密集操作时,消费端同步调用需要等待对方转换结束才返回,很消耗性能,这时选择异步调用和回调调用更合适。