Docker Fundamentals: Namespace

容器技术出现已经很久,只不过Docker容器平台的出现它变火了。Docker是第一个让容器能在不同机器之间移植的系统,它简化了打包应用的流程,也简化了打包应用的库和各种依赖。思考下整个OS的file system能直接被打包成一个简单的可移植的包,一开始的时候概念上还是很有趣的。 有时候我认为自己的阅读比较碎片化(short-term memory越来越少),所以我想把之前学习容器知识的一些基础技术再整理出来,也算是给自己学习的反馈。这个基础系列从Linux Namespace开始,后续会陆续介绍比如cgroup、aufs、devicemapper等技术。 参考 Namespace in operation Linux namespace man page Introduction to linux namespace 什么是Namespace 简单来说,linux namespace是Linux提供的一种内核级别环境隔离的方法。在早期的Unix中,提供了一种叫做chroot的系统调用:通过修改root目录把用户关到一个特定的目录下面。这种就是简单的隔离方式,也就是chroot内部的file system无法访问外部的内容。Linux Namespace在此基础之上,提供了对UTS、IPC、mount、network、PID、User等隔离机制。 这里可以简单举例,比如Linux的超级父进程的PID为1,如果我们可以把用户的进程空间关到某个进程分支之下,并且像chroot那样能够让下面的进程看到那个超级父进程的PID为1,而不同PID Namespace中的进程无法看到彼此,这样就能达到进程隔离。 Linux Namespace有以下的种类,供给后续参考(刚看有个印象就行): 分类 系统调用参数 相关内核版本 Mount namespaces CLONE_NEWNS Linux 2.4.19 UTS namespaces CLONE_NEWUTS Linux 2.6.19 IPC namespaces CLONE_NEWIPC Linux 2.6.19 PID namespaces CLONE_NEWPID Linux 2.6.24 Network namespaces CLONE_NEWNET 始于Linux 2.6.24 完成于 Linux 2.6.29 User namespaces CLONE_NEWUSER 始于 Linux 2.6.23 完成于 Linux 3.8) 其主要涉及到三个系统调用: clone(): 实现线程的系统调用,用来创建新的线程,并可通过涉及上述参数做到隔离 unshare(): 让某一个线程脱离某namespace setns(): 把某一个线程加到某namespace 如果读者你想看具体的实例,请自己man一下(关注一下自己的linux虚拟机内核),或者google一下,我这里贴一个clone()的source code: ...

April 1, 2021 · 11 min · 2282 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