Docker Fundamentals: Cgroup

Linux Namespace的技术解决了环境隔离的问题,不过这是虚拟化最基本的一步,我们另外需要解决对计算机资源使用上的隔离。说人话,就是虽然Namespace把我关到一个特定的环境,但是里面进程使用的CPU、内存、磁盘等计算资源实际上没有被限制。这个问题的解决,就要用到CGroup技术。 Linux CGroup全称是Linux Control Group,也是其内核的一个功能,用于限制、控制和分离一个进程group的资源。最早这个项目是2006年由谷歌的工程师发起的,最开始名称是process containers(工程容器),后面觉得内核中容器这个名词被用烂了,就改名为cgroup。 CGroup可以让你对系统中运行的进程的用户组分配资源-CPU时间、系统内存、网络带宽亦或者是这些的组合。同时,也可以监控你配置的cgroup,拒绝cgroup访问某些资源。主要提供的功能如下: Resource Limitation: 限制资源使用 Prioritization: 优先级控制,例如CPU使用和磁盘IO吞吐 Accounting:审计统计,主要用于计费 Control:挂起进程,恢复执行进程 在真正的实践当中,system admin一般会利用CGroup做以下的事: 对进程集合进行隔离,限制他们所消费的资源,例如绑定CPU core 为这组进程分配足够使用的内存 为这组进程分配响应的网络带宽和磁盘存储限制 限制访问某些设备(白名单) Linux实际上把CGroup实现成了一个文件系统,你可以mount。在linux环境输入下面的可以看到cgroup已经为你mount好: 1 2 3 4 5 6 7 8 9 10 11 12 derios@ubuntu:~$ mount -t cgroup cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,relatime,cpuset) cgroup on /sys/fs/cgroup/cpu type cgroup (rw,relatime,cpu) cgroup on /sys/fs/cgroup/cpuacct type cgroup (rw,relatime,cpuacct) cgroup on /sys/fs/cgroup/memory type cgroup (rw,relatime,memory) cgroup on /sys/fs/cgroup/devices type cgroup (rw,relatime,devices) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,relatime,freezer) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,relatime,blkio) cgroup on /sys/fs/cgroup/net_prio type cgroup (rw,net_prio) cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,net_cls) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,relatime,perf_event) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,relatime,hugetlb) 可以看到,在/sys/fs下有cgroup目录,这个目录下面有各种子目录:cpu,cpuset,memory…。这些都是cgroup的子系统,分别用来干不同的事。 ...

April 5, 2021 · 4 min · 848 words · Me

Go编程模式:Visitor(k8s)

概述 最近在看kubernetes的kubectl部分源码,记录一下其中用到的visitor编程模式(实际上kubectl主要用到了builder和visitor)。visitor模式是将算法和操作对象结构分离的一种方法。换句话说,这样的分离能够在不修改对象结构的情况下向原有对象新增操作,是符合开闭原则的。这个文章以一些例子去讨论kubectl中到底如何玩的。 从一个例子出发 写一个简单的Visitor模式示例: 我们的代码中有一个Visitor的函数定义,还有一个Shape接口,其需要使用 Visitor函数做为参数 我们的实例的对象 Circle和 Rectangle实现了 Shape 的接口的 accept() 方法,这个方法就是等外面给我传递一个Visitor。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package main import ( "encoding/json" "encoding/xml" "fmt" ) type Visitor func(shape Shape) type Shape interface { accept(Visitor) } type Circle struct { Radius int } func (c Circle) accept(v Visitor) { v(c) } type Rectangle struct { Width, Heigh int } func (r Rectangle) accept(v Visitor) { v(r) } 然后,我们实现两个Visitor,一个是用来做JSON序列化的,另一个是用来做XML序列化的: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func JsonVisitor(shape Shape) { bytes, err := json.Marshal(shape) if err != nil { panic(err) } fmt.Println(string(bytes)) } func XmlVisitor(shape Shape) { bytes, err := xml.Marshal(shape) if err != nil { panic(err) } fmt.Println(string(bytes)) } 下面是我们的使用Visitor这个模式的代码: ...

March 31, 2021 · 4 min · 696 words · Me

Kubernetes Handbook (Start & Pod)

使用minikube构建本地单节点k8s集群 minikube ssh kubectl cluster-info kubectl get nodes #查看节点信息 kubectl describe node minikube #详细信息 多节点k8s集群,使用Google K8s Engine 构建方式看GKE官网即可 k8s初步使用 kubectl run kubia –image=derios/kubia –port=8080 –generator=run/v1 --image=derios/kubia代表要运行的容器镜像 这里的--generator会被废弃,其含义指代的是创建一个ReplicationController而不是Deployment。 kubectl apply -f 更常用 kubectl get pods kubectl get pods -o wide 显示pod ip和pod的节点 如果使用GWE,可以访问集群的dashborad: kubectl clusert-info获取地址 gcloud container clusters describe kubia | grep -E “(username|password):“获取用户名和密码 如果仅仅使用minikube,则如下不需要任何凭证即可访问: 1 minikube dashboard Namespace相关操作 1 kubectl config set-context --current --namespace=my-namespace 创建服务对象,访问Web应用 如果使用minikube或者kubeadm等自定义k8s,loadbalancer是没有集成的,需要AWS或者Google Cloud。最好使用NodePort或者Ingress Controller。如果真要用minikube, 可以使用minikube tunnel解决, 或者minikube service kubia-http ...

March 31, 2021 · 6 min · 1100 words · Me

Docker Cheat Sheet

Books Docker in Action (English ver.) Docker入门到实践(中文) 速查 Docker Cheat Sheet 全量CLI 容器管理CLI 查看容器CLI 容器交互CLI 镜像管理CLI 镜像传输CLI DOCKERFILE主要命令 Dockerfile 基底 1 FROM ruby:2.2.2 变量 1 2 ENV APP_HOME/myapp RUN mkdir $APP_HOME 初始化 1 RUN bundle install 1 WORKDIR /myapp 1 2 VOLUME ["/data"] # Specification for mount point 1 2 ADD file.xyz /file.xyz COPY --chown=user:group host_file.xyz /path/container_file.xyz Onbuild 1 2 ONBUILD RUN bundle install # when used with another file 命令 1 2 EXPOSE 5900 CMD ["bundle", "exec", "rails", "server"] Entrypoint 1 ENTRYPOINT exec top -b Metadata 1 LABEL version="1.0" 1 2 LABEL "com.example.vendor"="ACME Incorporated" LABEL com.example.label-with-value="foo" 1 2 LABEL description="This text illustrates \ that label-values can span multiple lines." Docker Compose 基本用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # docker-compose.yml version: '2' services: web: build: . # build from Dockerfile context: ./Path dockerfile: Dockerfile ports: - "5000:5000" volumes: - .:/code redis: image: redis 指令 1 2 docker-compose start docker-compose stop 1 2 docker-compose pause docker-compose unpause 1 2 3 docker-compose ps docker-compose up docker-compose down Reference(例子) 构建 1 2 3 web: # build from Dockerfile build: . 1 2 3 4 # build from custom Dockerfile build: context: ./dir dockerfile: Dockerfile.dev 1 2 3 4 5 6 # build from image image: ubuntu image: ubuntu:14.04 image: tutum/influxdb image: example-registry:4000/postgresql image: a4bc65fd 端口 1 2 3 ports: - "3000" - "8000:80" # guest:host 1 2 # expose ports to linked services (not to host) expose: ["3000"] 指令 1 2 3 # command to execute command: bundle exec thin -p 3000 command: [bundle, exec, thin, -p, 3000] 1 2 3 # override the entrypoint entrypoint: /app/start.sh entrypoint: [php, -d, vendor/bin/phpunit] 环境变量 1 2 3 4 5 # environment vars environment: RACK_ENV: development environment: - RACK_ENV=development 1 2 3 # environment vars from file env_file: .env env_file: [.env, .development.env] 依赖 1 2 3 4 5 # makes the `db` service available as the hostname `database` # (implies depends_on) links: - db:database - redis 1 2 3 # make sure `db` is alive before starting depends_on: - db 其他选项 1 2 3 4 # make this service extend another extends: file: common.yml # optional service: webapp 1 2 3 volumes: - /var/lib/mysql - ./_data:/var/lib/mysql 高级特性 打标签 1 2 3 4 services: web: labels: com.example.description: "Accounting web app" DNS服务器 1 2 3 4 5 6 services: web: dns: 8.8.8.8 dns: - 8.8.8.8 - 8.8.4.4 设备绑定 1 2 3 4 services: web: devices: - "/dev/ttyUSB0:/dev/ttyUSB0" 外部链接 1 2 3 4 5 services: web: external_links: - redis_1 - project_db_1:mysql 主机设置 1 2 3 4 services: web: extra_hosts: - "somehost:192.168.1.100" Services 1 2 3 4 5 6 7 8 9 10 11 # To view list of all the services runnning in swarm docker service ls # To see all running services docker stack services stack_name # to see all services logs docker service logs stack_name service_name # To scale services quickly across qualified node docker service scale stack_name_service_name=replicas Clean up 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # To clean or prune unused (dangling) images docker image prune # To remove all images which are not in use containers , add - a docker image prune -a # To Purne your entire system docker system prune # To leave swarm docker swarm leave # To remove swarm ( deletes all volume data and database info) docker stack rm stack_name # To kill all running containers docker kill $(docekr ps -q )

March 30, 2021 · 3 min · 584 words · Me

Life of an HTTP request in a Go server

这篇文章的启发是我在阅读Go的http源码时获得的,之前对这块缺乏深入的了解,这篇文章会结合源码讨论包括典型http request的路由,还会涉及到一些并发和中间件的issue。 我们先从一个简单的go server谈起,下面的代码从https://gobyexample.com/http-servers 截取: 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 package main import ( "fmt" "net/http" ) func hello(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "hello\n") } func headers(w http.ResponseWriter, req *http.Request) { for name, headers := range req.Header { for _, h := range headers { fmt.Fprintf(w, "%v: %v\n", name, h) } } } func main() { http.HandleFunc("/hello", hello) http.HandleFunc("/headers", headers) http.ListenAndServe(":8090", nil) } 追踪请求的生命周期我们从http.ListenAndServe这个方法开始,下面的图示说明了这一层的调用关系: 这里实际上inlined了一些代码,因为初始的代码有很多其他的细节不好追踪。 ...

February 20, 2021 · 3 min · 639 words · Me