• 深入剖析Kubernetes学习笔记:深入理解镜像(08)


    一、Python 应用案例环境

    [root@k8s-node1 Flask]# pwd
    /opt/Dockerfile/Flask
    [root@k8s-node1 Flask]# ll
    total 12
    -rw-r--r-- 1 root root 352 Feb  1 10:08 app.py
    -rw-r--r-- 1 root root 518 Feb  1 10:20 Dockerfile
    -rw-r--r-- 1 root root   6 Feb  1 10:09 requirements.txt
    [root@k8s-node1 Flask]# cat app.py
    from flask import Flask
    import socket
    import os
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello():
        html = "<h3>Hello {name}!</h3>" 
               "<b>Hostname:</b> {hostname}<br/>"
        return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname())
    
    if __name__ == "__main__":
        app.run(host='0.0.0.0', port=80)
    [root@k8s-node1 Flask]# cat requirements.txt
    Flask
    [root@k8s-node1 Flask]# cat Dockerfile
    # 使用官方提供的 Python 开发镜像作为基础镜像
    FROM python:2.7-slim
    
    # 将工作目录切换为 /app
    WORKDIR /app
    
    # 将当前目录下的所有内容复制到 /app 下
    ADD . /app
    
    # 使用 pip 命令安装这个应用所需要的依赖
    RUN pip install --trusted-host pypi.python.org -r requirements.txt
    
    # 允许外界访问容器的 80 端口
    EXPOSE 80
    
    # 设置环境变量
    ENV NAME World
    
    # 设置容器进程为:python app.py,即:这个 Python 应用的启动命令
    CMD ["python", "app.py"]
    

    Dockerfile的设计思想,是使用一些标准的原语(即大写高亮的词语),描述我们所要构建的Docker镜像,并且这些原语,都是按顺序处理的

    需要注意的是,Dockerfile中的每个原语执行后,都会生成一个对应的镜像层

    [root@k8s-node1 Flask]# docker images|grep docker.io/python
    docker.io/python

    二、启动容器以及相关操作

    1、启动容器

    $ docker run -p 4000:80 helloworld

    2、查看进程

    $ docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED
    4ddf4638572d        helloworld       "python app.py"     10 seconds ago
    

    3、测试是否可用

    $ curl http://localhost:4000
    <h3>Hello World!</h3><b>Hostname:</b> 4ddf4638572d<br/>

    4、上传镜像到仓库

    [root@k8s-node1 ~]# docker exec -it 900e7b6e1984 /bin/sh#
    # touch test.txt#
    # exit#
    [root@k8s-node1 ~]# docker tag helloworld 10.0.128.4:500/helloworld:v1#
    [root@k8s-node1 ~]# docker push 10.0.128.4:500/helloworld:v1#
    The push refers to a repository [10.0.128.4:500/helloworld]#
    Get https://10.0.128.4:500/v1/_ping: dial tcp 10.0.128.4:500: getsockopt: connection refused                                     "/bin/sh"                2 days ago          Exited (137) 2 days ago  grave_mclean#

    三、docker exec 是怎么做到进入容器里的呢?

    1、docker exec 的实现原理

    [root@k8s-node1 Flask]# docker exec -it 43c49903582e /bin/sh
    # pwd
    /app
    # ls
    Dockerfile  app.py  requirements.txt  test.txt
    

    这里,我使用了docker exec 命令进入到了容器当中,在了解了Linux Namespace的隔离机制后,你应该会很自然地想到一个问题
    docker exec 是怎么做到进入容器里的呢?

    [root@k8s-node1 Flask]# docker inspect --format '{{ .State.Pid }}' 43c49903582e
    2400
    [root@k8s-node1 opt]# ls -l /proc/2400/ns
    total 0
    lrwxrwxrwx 1 root root 0 Feb 20 17:20 ipc -> ipc:[4026532495]
    lrwxrwxrwx 1 root root 0 Feb 20 17:19 mnt -> mnt:[4026532493]
    lrwxrwxrwx 1 root root 0 Feb 20 17:19 net -> net:[4026532498]
    lrwxrwxrwx 1 root root 0 Feb 20 17:20 pid -> pid:[4026532496]
    lrwxrwxrwx 1 root root 0 Feb 20 17:23 user -> user:[4026531837]
    lrwxrwxrwx 1 root root 0 Feb 20 17:20 uts -> uts:[4026532494]

    这也就意味着:一个进程,可以选择加入到某个进程已有的 Namespace 当中,从而达到“进入”这个进程所在容器的目的,这正是 docker exec 的实现原理

    2、docker exec 的实现原理刨析实验

    1、依赖文件

    _startmainopen@@GLIBC_2.2.5perror@@GLIBC_2.2.5_Jv_RegisterClassesexecvp@@GLIBC_2.2.5exit@@GLIBC_2.2.5__TMC_END___ITM_registerTMCloneTable_init
    [root@k8s-node1 opt]# cat set_ns.c
    #define _GNU_SOURCE
    #include <fcntl.h>
    #include <sched.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE);} while (0)
    
    int main(int argc, char *argv[]) {
        int fd;
    
        fd = open(argv[1], O_RDONLY);
        if (setns(fd, 0) == -1) {
            errExit("setns");
        }
        execvp(argv[2], &argv[2]);
        errExit("execvp");
    }
    

    2、执行

    [root@k8s-node1 opt]# gcc -o set_ns set_ns.c
    [root@k8s-node1 opt]# ./set_ns /proc/2400/ns/net /bin/bash
    [root@k8s-node1 opt]# ifconfig
    eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 172.16.6.2  netmask 255.255.255.0  broadcast 0.0.0.0
            inet6 fe80::42:acff:fe10:602  prefixlen 64  scopeid 0x20<link>
            ether 02:42:ac:10:06:02  txqueuelen 0  (Ethernet)
            RX packets 8  bytes 648 (648.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 8  bytes 648 (648.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
            inet 127.0.0.1  netmask 255.0.0.0
            inet6 ::1  prefixlen 128  scopeid 0x10<host>
            loop  txqueuelen 1  (Local Loopback)
            RX packets 0  bytes 0 (0.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 0  bytes 0 (0.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    

    3、查看进程的进程的 Namespace

    [root@k8s-node1 opt]# ps aux|grep /bin/bash
    root       2615  0.1  0.0 116204  2876 pts/0    S    17:28   0:00 /bin/bash
    root       2652  0.0  0.0 112660   972 pts/0    R+   17:28   0:00 grep --color=auto /bin/bash
    [root@k8s-node1 opt]# ls -l /proc/2615/ns/net
    lrwxrwxrwx 1 root root 0 Feb 20 17:30 /proc/2615/ns/net -> net:[4026532498]
    [root@k8s-node1 opt]# ls -l /proc/2400/ns/net
    lrwxrwxrwx 1 root root 0 Feb 20 17:19 /proc/2400/ns/net -> net:[4026532498]
    

    转了一个大圈子,我其实是为你详细解读了这个操作背后,linux namespace更具体的工作原理

    这种通过操作系统进程相关的知识,逐步刨析Docker容器的方法,是理解容器的一个关键思路,希望你一定要掌握

  • 相关阅读:
    Java之IO流
    Servlet中Session的用法
    Servlet中Cookie的用法
    HTML的表单元素和input元素
    Servlet第一个实例之用户登录网址
    Servlet的生命周期和三种实现方式
    写一下近期的计划(工作)
    RxJava的基础知识
    actionbar、toolbar、menu之间的关系
    ButterKnife的基础知识
  • 原文地址:https://www.cnblogs.com/luoahong/p/10408208.html
Copyright © 2020-2023  润新知