筱团Blog筱团のBlog

Docker 详细

筱团·2022-09-08 01:30·196 次阅读

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 举例)
Copy
# 将 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 下载#

Copy
$ 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 用法#

基础命令
Copy
# 登入 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
镜像命令
Copy
# 查看全部 docker 镜像 docker image ls # 删除 docker 镜像 docker image rm 镜像名称 # 构建本地镜像(由当前目录下的 Dockerfile 文件构建,构建镜像名为 tuanzi/test) docker build . -t tuanzi/test
进程命令
Copy
# 启动 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 文件:
Copy
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 文件:
Copy
#! /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 文件:
Copy
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

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

posted @   筱团  阅读(196)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
点击右上角即可分享
微信分享提示
目录