原文内容:https://gitee.com/dev-99cloud/training-kubernetes ,在此基础上有新增。

Lesson 01:LXC & Docker

1.1 什么是 Linux 容器?

  • 开发技术的演进

  • 演进(虚拟化和容器技术)是为了解决什么问题?资源隔离 & 资源限制

  • 什么是 Linux 容器?namespace & cgroup

  • 什么是 lxc namespace?

  • 什么是 CGroup?

  • Mac 和 Win 上 有容器技术么?

1.2 容器和虚拟机有何区别?

  • 原理
    • 一个 KVM 虚拟机就是一个 KVM 进程,N Core 就是 N 个线程
    • 一个容器是一组被 LXC API 隔离+限制约束的进程组,容器中的进程就是宿主机操作系统中的进程,一一对应
  • 应用场景
    • 不同的内核
    • 不同的操作系统
    • 不同的 CPU 指令集

1.3 Docker 和容器技术有什么关系?

  • 什么是 DockerQuickStart )?

  • Docker 和容器有什么关系( why Linus don't care docker )?

  • Docker 有哪些竞争产品?CRI-O ?,其它的容器命令行工具:GithubGitee

1.4 Docker 的架构和概念空间是怎样的?

  • 概念空间

  • 模块架构

1.5 什么是所谓的安全容器技术?

  • 容器的天然不安全与天然安全

  • Kata ContainerPDF

  • 竞争者:gVisor / firecracker / rustVM

1.6 实验:Docker Quick Start

1.6.1 centos 7

  • 在 centos 7

    # 移除旧的 docker 版本
    yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
    
    yum install -y yum-utils
    
    yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
    
    yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
    
    # 设置为开机启动并且立刻启动服务
    systemctl enable docker --now
    
    # run hello-world
    docker run hello-world
    

1.6.1.1 在 centos7 上构建镜像

  • 如何创建一个镜像?如何启动和调试容器?GithubGitee

    $ mkdir ~/test
    $ cd ~/test
    $ wget https://gitee.com/dev-99cloud/lab-openstack/raw/master/src/docker-quickstart/app.py
    $ wget https://gitee.com/dev-99cloud/lab-openstack/raw/master/src/docker-quickstart/requirements.txt
    $ wget https://gitee.com/dev-99cloud/lab-openstack/raw/master/src/docker-quickstart/Dockerfile
    
    # 如果是 CentOS 7.x 需要安装下 python3
    $ yum install python3 python3-pip
    
    # pip3 install -r requirements.txt
    $ pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
    
    $ python3 app.py
    * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
    
    # 此时可以从浏览器访问 http://<ip>/
    
    $ docker build --tag=friendlyhello .
    
    # 可以看一下本地镜像列表
    $ docker images
    
    # 删除已存在的 testFlask 容器
    $ docker rm testFlask 2>/dev/null
    
    # 以 99cloud/friendlyhello:3.9.6 镜像启动 testFlask 容器,容器内的 80 端口映射到宿主机的 4000 端口
    # 如果带 --rm 参数,stop 之后,就会直接 rm 容器
    $ docker run -p 4000:80 --name=testFlask 99cloud/friendlyhello:3.9.6
     * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
    
    # 此时可以从浏览器访问 http://<ip>:4000
    
    # 如果要跑在后台,可以加 -d 参数
    $ docker run -d -p 4000:80 --name=testFlask 99cloud/friendlyhello:3.9.6
    
    # 进入容器调试
    $ docker exec -it testFlask /bin/bash
    root@4224b69e7ee3:/app# env
    HOSTNAME=4224b69e7ee3
    PWD=/app
    HOME=/root
    NAME=World
    ...
    
    root@4224b69e7ee3:/app# ps -ef
    
    # 查看容器日志
    $ docker logs -f testFlask
    
    # 结束容器
    $ docker stop testFlask
    
    # 启动容器
    $ docker start testFlask
    
    # 从容器生成新的镜像
    $ docker stop testFlask
    $ docker commit testFlask test-new
    
    # 删除容器
    $ docker rm testFlask
    
    # 删除镜像
    $ docker rmi friendlyhello
    $ docker rmi 99cloud/friendlyhello:3.9.6
    

1.6.2 ubuntu Ubuntu 18.04 / Ubuntu 20.04

  • 在 Ubuntu 18.04 / Ubuntu 20.04

    # 更新依赖仓库
    apt-get update -y || yum update -y
    
    # 安装 Docker
    apt-get install docker.io -y || yum install docker -y
    systemctl enable docker
    systemctl start docker
    
    # 检查 docker 服务状态
    systemctl status docker
    
    # ubuntu 中需要把 docker 的 cgroup driver 改成 systemd
    # !! centos 老版本的 docker 1.13 默认就是 systemd,不要修改这个文件,改了 docker 会起不来,保持 {} 就好,新版本的 docker 24+ 需要改
    vi /etc/docker/daemon.json
    
    {
      "exec-opts": ["native.cgroupdriver=systemd"]
    }
    
    systemctl restart docker
    
    # run hello-world
    docker run hello-world
    

    Note:2021 年 7 月之后,ubuntu 环境 kubeadmin 默认都是 1.22+ 版本,因此需要将 docker 的 cgroup driver 改成 systemd(原来是 cgroup)。如果不改,后续 kubeadm init 时,会报错:[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp [::1]:10248: connect: connection refused.

    检查 journalctl -x -u kubelet,可以看到:Aug 07 15:10:45 ckalab2 kubelet[11394]: E0807 15:10:45.179485 11394 server.go:294] "Failed to run kubelet" err="failed to run Kubelet: misconfiguration: kubelet cgroup driver: \"systemd\" is different from docker cgroup driver: \"cgroupfs\""

    看官方文档:https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/configure-cgroup-driver/In v1.22, if the user is not setting the cgroupDriver field under KubeletConfiguration, kubeadm will default it to systemd.

    所以我们需要把 docker 的 cgroup driver 改成 systemd

    修改步骤参考:https://stackoverflow.com/questions/43794169/docker-change-cgroup-driver-to-systemd

    修改完成后,检查一下 docker cgroup,确保 docker cgroup 是 systemd 了:sudo docker info | grep -i cgroup

  • Docker 官方入门参考资料

1.7 Docker 的网络模型

  • Bridge 模式

    # docker 容器实现没有把 network namespaces 放到标准路径 `/var/run/netns` 下,所以 `ip netns list` 命令看不到
    
    # 但是可以看 `ll /proc/<pid>/ns`,两个进程的 namespaces id 相同说明在同一个 namespaces
    
    [root@cloud025 ns]# ll /proc/2179/ns/
    total 0
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 ipc -> ipc:[4026531839]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 mnt -> mnt:[4026531840]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 net -> net:[4026531956]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 pid -> pid:[4026531836]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 user -> user:[4026531837]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 uts -> uts:[4026531838]
    
    # 做个软链接,就可以看到 netns 了
    
    [root@cloud025 ns]# docker ps
    CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS                  NAMES
    07297a3ac7ea        nginx:latest                  "/docker-entrypoin..."   29 minutes ago      Up 29 minutes       80/tcp                 devtest
    0935b08509a4        test-new                      "python app.py"          35 minutes ago      Up 35 minutes       0.0.0.0:5000->80/tcp   testNew
    c23c76dd779c        99cloud/friendlyhello:3.9.6   "python app.py"          37 minutes ago      Up 36 minutes       0.0.0.0:4000->80/tcp   testFlask
    
    [root@cloud025 ns]# docker inspect testFlask | grep -i pid
                "Pid": 1159,
                "PidMode": "",
                "PidsLimit": 0,
    [root@cloud025 ns]# mkdir -p /var/run/netns
    [root@cloud025 ns]# ln -s /proc/1159/ns/net /var/run/netns/testFlask
    [root@cloud025 ns]# ip netns list
    testFlask (id: 0)
    devtest (id: 2)
    testNew (id: 1)
    
    # 进入对应的 namespaces,看 ip,pod namespace 里虚拟网卡的 link-netnsid 始终等于 0
    [root@cloud025 ns]# ip netns exec testNew ip a
    ...
    44: eth0@if45: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 172.17.0.3/16 scope global eth0
          valid_lft forever preferred_lft forever
        inet6 fe80::42:acff:fe11:3/64 scope link
          valid_lft forever preferred_lft forever
    
    # 在 root namespaces 中 ip a,可以看到 link-netnsid = 1,1 是前面 ip netns list 里的 namespaces id
    [root@cloud025 ns]# ip a
    45: vethb6d08be@if44: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
        link/ether 0e:d9:14:d1:86:98 brd ff:ff:ff:ff:ff:ff link-netnsid 1
        inet6 fe80::cd9:14ff:fed1:8698/64 scope link
          valid_lft forever preferred_lft forever
    
    # 这是一对 veth pair,看他们的序号和 if 可以发现
    
    # 看网桥,可以看到这个 root namespaces 的虚拟网卡绑在 docker0 网桥上
    # 在 CentOS 上,需要安装一下:yum install bridge-utils
    [root@cloud025 ~]# brctl show
    bridge name	bridge id		STP enabled	interfaces
    docker0		8000.02428c25c112	no		vethb6d08be
    virbr0		8000.525400d583b9	yes		virbr0-nic
    

    可以尝试对虚拟网卡抓包,看进出容器的流量

  • Host 模式

  • CNM

1.8 Docker 的存储模型

  • Mount 模式

    [root@cka-studenta-1 ~]# mkdir testhaha
    [root@cka-studenta-1 ~]# docker run -d -it --name devtest -v "$(pwd)"/testhaha:/app nginx:latest
    Unable to find image 'nginx:latest' locally
    Trying to pull repository docker.io/library/nginx ...
    latest: Pulling from docker.io/library/nginx
    ...
    7897813b7065a0390db335656443782895155655f263de6ee8264a6f2185fe16
    
    [root@cka-studenta-1 ~]# docker ps
    CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS                  NAMES
    7897813b7065        nginx:latest                  "/docker-entrypoin..."   6 seconds ago       Up 4 seconds        80/tcp                 devtest
    b667b8e2f90b        99cloud/friendlyhello:3.9.6   "python app.py"          3 hours ago         Up 3 hours          0.0.0.0:4000->80/tcp   testFlask
    [root@cka-studenta-1 ~]# docker exec -it 7897813b7065 /bin/sh
    
    # ls
    app  bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
    # cd app
    # ls
    # echo fsdfasfdasdfsa > xxxxxxxx.txt
    # exit
    
    [root@cka-studenta-1 ~]# ls
    test  testhaha
    [root@cka-studenta-1 ~]# cd testhaha/
    [root@cka-studenta-1 testhaha]# ls
    xxxxxxxx.txt
    [root@cka-studenta-1 testhaha]# cat xxxxxxxx.txt
    fsdfasfdasdfsa
    
  • Volumn 模式