• Docker 详细


    Prerequisite

    很久之前写过 Docker 的博客,但那时也只是随意了解一下,现在打算重新学习(但我依然没有实际场景需要用到,因此以后可能还会再写一次)

    参考文章:廖雪峰【一文读懂Docker原理】
    参考文章:Install Docker Engine on Ubuntu
    参考文章:浅析 Dockerfile 中 RUN、CMD 以及 ENTRYPOINT 指令的异同
    参考文章:Docker 常用命令
    参考视频:Docker技术从0到1全覆盖(对应笔记
    参考报错:failed to solve with frontend dockerfile.v0: failed to create LLB definition
    使用镜像源:清华大学开源软件镜像站

    Docker 原理

    Docker 的容器是一种对进程进行隔离的运行环境(进程是 Linux 操作系统执行任务的最小单元)
    Docker 本质就是帮助我们设置好隔离环境后,启动容器中不同隔离的进程(Linux 系统本身负责隔离)

    隔离的方法有三种:

    • 第一种就是进程之间看不到彼此,这是由 Linux 的 Cgroup 机制实现的。进程隔离的结果就是以隔离方式启动的进程看到的自身进程 ID 总是 1,且看不到系统的其他进程。
    • 第二种就是隔离系统真实的文件系统。Docker 利用 Linux 的 mount 机制,给每个隔离进程挂载了一个虚拟的文件系统,使得一个隔离进程只能访问这个虚拟的文件系统,无法看到系统真实的文件系统。至于这个虚拟的文件系统应该长什么样,这就是制作 Docker 镜像要考虑的问题。比如我们的 Python 程序要正常运行,需要一个 Python3 解释器,需要把用到的第三方库如 psutil 引入进来,这些复杂的工作被简化为一个 Dockerfile,再由 Docker 把这些运行时的依赖打包,就形成了 Docker 镜像。我们可以把一个 Docker 镜像看作一个 zip 包,每启动一个进程,Docker 都会自动解压 zip 包,把它变成一个虚拟的文件系统。
    • 第三种就是网络协议栈的隔离(以下面要运行 ZooKeeper 和 Kafka 举例)
    # 将 zookeeper 进程的端口号映射到宿主机,从而在宿主机上访问 zookeeper
    # 但这只是 zookeeper 进程的端口映射,如果需要映射多个进程,需要利用 Docker Compose
    docker run -p 2181:2181 zookeeper:latest
    

    因为 ZooKeeper 和 Kafka 都有监听的端口,但 Linux 可以为进程隔离网络,Docker 默认启动的 ZooKeeper 和 Kafka 进程拥有自己的网络名字空间,与宿主机不同。利用 Docker Compose,把 ZooKeeper 和 Kafka 运行在同一个网络名字空间里,并通过 zookeeper:2181 来访问 ZooKeeper 端口,让 Docker 自动把 zookeeper 名字解析为动态分配的 IP 地址

    Docker Linux 下载

    $ sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
    
    $ sudo mkdir -p /etc/apt/keyrings
    
    $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    
    $ echo \
       "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \
       $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    $ sudo apt-get update
    
    $ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
    

    Docker 用法

    基础命令
    # 登入 Docker Hub(用户名为:courserli)
    docker login -u courserli
    
    # 从 Docker Hub 下载镜像(名字为:courserli 的 hello-docker)
    docker pull courserli/hello-docker
    
    # 分享镜像到 Docker Hub(名字为:courserli 的 hello-docker,默认 tag 为:latest)
    docker push courserli/hello-docker
    
    # 清除缓存(会停止全部容器的运行)
    docker system prune
    
    镜像命令
    # 查看全部 docker 镜像
    docker image ls
    
    # 删除 docker 镜像
    docker image rm 镜像名称
    
    # 构建本地镜像(由当前目录下的 Dockerfile 文件构建,构建镜像名为 tuanzi/test)
    docker build . -t tuanzi/test
    
    进程命令
    # 启动 docker 容器(-it 是交互式操作终端,sagemath/sagemath 是镜像名,--rm 表示在容器退出时自动清理容器内部的文件系统)
    docker run -it --rm sagemath/sagemath
    
    # 启动 docker 容器(-d 后台运行,-p 端口映射,前者为容器内部端口,后者为映射本地端口,如果镜像名在本地找不到,会自动联网 pull)
    docker run -d -p 80:80 docker/getting-started
    
    # 启动 docker 容器(-v 是挂载路径,后接挂载本地路径和容器路径)
    docker run -it --rm -v 路径:/share/data tuanzi/test
    
    # 在运行的 docker 容器中执行命令(ls 就是执行的命令)
    docker exec -it nginx ls
    
    # 进入正在运行的 docker 容器(/bin/bash 命令相当于弹出 shell 来,可以看作是进入了)
    docker exec -it nginx /bin/bash
    
    # 查看正在运行的 docker 容器进程
    docker ps
    
    # 查看已经停止的 docker 容器进程
    docker ps -a
    
    # 停止 docker 容器进程(参数是 CONTAINER ID)
    docker rm -f 042c94072180
    
    Docker file

    这里以一个实际的例子回顾,这是我的本地的目录

    • test
      • db
        • data
      • Dockerfile
      • run.sh

    执行命令的路径与 Dockerfile 同级

    • Dockerfile 文件:
    FROM ubuntu:16.04
    
    COPY run.sh /run.sh
    
    VOLUME /share/data
    
    RUN useradd nss
    
    RUN sed -i s@/deb.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list && \
        sed -i s@/security.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list
        
    RUN ["apt-get", "update", "-y"]
    
    WORKDIR /home/nss
    
    RUN chmod +x /run.sh
    
    ENV FLAG=NSSCTF{test_flag}
    
    EXPOSE 80/tcp
    
    CMD [ "helloworld" ]
    
    USER nss
    
    ENTRYPOINT [ "/run.sh" ]
    
    • run.sh 文件:
    #! /bin/bash
    echo "Run with user `whoami`"
    echo "Run with directory `pwd`"
    echo "Run with environment $FLAG"
    echo "Run with parameter $1"
    tail -f /dev/null
    
    read v
    while [ "$v" == "n" ];
    do
        cat /share/data/data
        read v
    done;
    

    关于内容的具体细节可以看这篇文章,我来特别说一下:

    • 如何挂载?

    一般构建的命令是 docker build . -t tuanzi/test,然后运行是 docker run -it --rm tuanzi/test;正如 Dockerfile 文件中的 VOLUME /share/data 所描述,我需要在命令行中添加挂载路径,即 docker run -it --rm -v 路径:/share/data tuanzi/test,此时容器内 data 文件的内容会随着本地 data 文件而改变

    • 如何直接进入容器中(而不是终端交互)?

    去掉 Dockerfile 文件中的 RUN、CMD、ENTRYPOINT 的命令,或者执行 docker run -it --rm tuanzi/test /bin/sh 命令,用于覆盖掉 Dockerfile 文件中的 ENTRYPOINT

    针对 RUN、CMD 和 ENTRYPOINT,大佬给出了总结:

    • RUN、CMD 和 ENTRYPOINT 指令都可以用来执行具体的命令
    • RUN 指令是在 Docker 镜像构建时发挥作用, 可以使用多个该命令, 且执行结果会记录到镜像中
    • CMD 和 ENTYPOINT 指令是在容器启动时自动执行, 均只有最后一个该指令有效, 且均可以在 docker run 中被覆盖
    • ENTRYPOINT 指令和 CMD 的区别在于使用 ENTRYPOINT 时 CMD 指令会被作为其默认参数, 而用户也可以在启动容器时通过覆盖 CMD 指令来输入参数; 此外, 这也意味着 ENTRYPOINT 指令的内容不易被用户命令覆盖

    Docker Compose

    Docker Compose 的意义在于结合多个 Docker 容器,比如搭建一个网站(Linux + Nginx + Mysql + php)

    这里再以一个实际的例子回顾,这是我的本地的目录

    • test
      • mqsql
        • app.sql
        • Dockerfile
      • nginx
        • default.conf
        • Dockerfile
      • php1
        • src
          • index.php
        • Dockerfile
      • php2
        • src
        • Dockerfile
      • docker-compose.yml

    执行命令的路径与 docker-compose 同级

    • docker-compose 文件:
    version: '3'
    # Linux + Nginx + Mysql + PHP
    services:
      nginx:
        build: ./nginx
        container_name: "nginx"
        ports:
          - 8080:80
        restart: always
        cap_add:
            - SYS_ADMIN
        depends_on:
          - php1
          - php2
        networks:
          default:
          my_net:
            ipv4_address: 172.2.0.3
      php1:
        build: ./php1
        container_name: nssctf
      php2:
        build: ./php2
        container_name: xenny
      mysql:
        build: ./mysql
        container_name: mysql
        environment:
          - MYSQL_ROOT_PASSWORD=password
          - MYSQL_DATABASE=app
        command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --sql-mode='' --max-execution-time=1000
    
    networks:
      my_net:
        driver: bridge
        internal: true
        ipam:
          config:
            - subnet: 172.2.0.0/16
    

    关于内容的具体细节依然可以看这篇文章,我就不赘述了

  • 相关阅读:
    with异常
    内建函数的重写
    布尔测试函数重写
    函数重写
    类中的对象属性管理函数
    类中的迭代器__iter__
    多继承
    多态
    封装
    深入理解Java:类加载机制及反射
  • 原文地址:https://www.cnblogs.com/CourserLi/p/16549995.html
Copyright © 2020-2023  润新知