2013 年 3 月 15 日,在北美的圣克拉拉市召开了一场 Python 开发者社区的主题会议 PyCon,研究和探讨各种 Python 开发技术和应用,
在当天的会议日程快结束时,有一位名为 Solomon Hykes 开发者(Docker 公司的创始人)在“闪电演讲”(lighting talk)的小环节,用了 5 分钟的时间,做了题为 “The future of Linux Containers” 的演讲,展示了 Docker 技术,不过临近末尾因为超时而被主持人赶下了台。
使用 Docker 有两个选择:
Ubuntu 安装命令:
sudo apt install -y docker.io
启动 Docker 服务并将当前用户加入 Docker 组:
sudo service docker start #启动docker服务
sudo usermod -aG docker ${USER} #操作 Docker 必须要有 root 权限,直接使用 root 用户不够安全
验证 Docker 是否安装成功,成功会输出 Docker 客户端和服务器各自的版本信息:
docker version
显示当前 Docker 系统相关的信息,例如 CPU、内存、容器数量、镜像数量、容器运行时、存储文件系统等等:
docker info
所有的 Docker 操作都是如下形式:以 docker 开始,跟上一个具体的子命令。
列出当前系统里运行的容器:
docker ps
查看所有的容器:
docker ps -a
获取帮助文档信息,查看命令清单和更详细的说明:
docker help
从外部的镜像仓库(Registry)拉取一个镜像:
docker pull
列出当前 Docker 所存储的所有镜像:
docker images
从镜像中启动容器:
docker run
拉取 busybox 镜像:
docker pull busybox
从 busybox 镜像中启动容器,并执行 echo 命令:
docker run busybox echo hello world

客户端 Docker client 接收命令行的输入,并与 Docker Engine 里的后台服务 Docker daemon 通信。
镜像存储在远端的仓库 Registry 里,客户端可以通过 build、pull、run 等命令向 Docker daemon 发送请求。
Docker daemon 是容器和镜像的管理者,负责从远端拉取镜像、在本地存储镜像,还有从镜像生成容器、管理容器等所有功能,是实际命令的执行者。
展示 Docker client 到 Docker daemon 再到 Registry 的详细工作流程:
docker run hello-world
该命令的输出解析:
Docker 的起源
Docker 的产品
Docker 的架构和执行流程
Docker 的相关命令
广义上,容器技术是动态的容器、静态的镜像和远端的仓库三者的组合。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q9S7dQwt-1693031721772)(img/Docker与集装箱.png)]
集装箱:集装箱的作用是标准化封装各种货物,一旦打包完成之后,就可以从一个地方迁移到任意的其他地方。
容器:容器封装的运行中的应用程序(进程),把进程与外界隔离开,让进程与外部系统互不影响。即,容器是被隔离的进程。
安全
虽然 Linux 提供了用户权限控制,能够限制进程只访问某些资源,但这个机制还是比较薄弱的,和真正的“隔离”需求相差得很远。
使用容器技术,可以让应用程序运行在一个有严密防护的“沙盒”(Sandbox)环境之内,它只可以在这个环境里自由活动,从而保证了容器外系统的安全。
资源隔离
容器技术的另一个本领就是为应用程序加上资源隔离,在系统里切分出一部分资源,让它只能使用指定的配额,比如只能使用一个 CPU,只能使用 1GB 内存等等,可以避免容器内进程的过度系统消耗,充分利用计算机硬件,让有限的资源能够提供稳定可靠的服务。
都是虚拟化技术。
目的都是隔离资源,保证系统安全,尽量提高资源的利用率。
实际上,Docker 只是辅助建立隔离环境,容器并不直接运行在 Docker 上,而是基于 Linux 操作系统运行。
实现的角度:
容器和虚拟机可以结合使用,也可以独立使用。
隔离的实现:
容器实现了与外部系统的隔离。
容器限制了进程能访问的资源,并且相比于虚拟机更加轻量级和高效。
容器的基本实现:namespace、cgroup、chroot
容器是由操作系统动态创建的,因此需要把初始运行环境固定下来,保存成一个静态的文件,以便存放、传输、版本化管理了。
镜像:镜像打包了应用程序,不仅有基本的可执行文件,还有应用运行时的整个系统环境。
容器化应用:应用程序不直接和操作系统打交道,而是封装成镜像,再交给容器环境去运行。
镜像和容器的关系:
镜像就是静态的应用容器,容器就是动态的应用镜像,可以相互转换。
获取一个打包了 busybox 的镜像:
docker pull busybox
定位镜像,名字+标签:
采用名字+标签的方式,拉取镜像:
docker pull alpine:3.15
docker pull ubuntu:jammy
docker pull nginx:1.21-alpine
docker pull nginx:alpine
docker pull redis
image id :
删除镜像:
docker rmi redis # 使用名字删除
docker rmi sx2 # 采用id删除
将静态的镜像运行成为容器:
docker run 设置参数 镜像id|镜像名 运行命令
-h srv 容器的运行参数,alpine 镜像名,hostname 表示在容器里运行的“hostname”程序。
docker run -h srv alpine hostname
-it:表示开启一个交互式操作的 Shell,可以直接进入容器内部
-d:表示让容器在后台运行
–name: 可以为容器起一个名字,不用这个参数,Docker 会分配一个随机的名字。
eg:
docker run -d nginx:alpine # 后台运行Nginx
docker run -d --name red_srv redis # 后台运行Redis
docker run -it --name ubuntu 2e6 sh # 使用IMAGE ID,登录Ubuntu18.04
查看容器运行的状态:
docker ps
CONTAINER ID: 唯一标识容器
强制停止容器:
docker stop 容器名字|CONTAINER ID
再次启动停止的程序:
docker start 容器名字|CONTAINER ID
删除容器(而非镜像):
docker rm 容器名字|CONTAINER ID
自动删除不需要的容器(只要运行完毕就自动清除:
docker run -d --rm 容器名字|CONTAINER ID # --rm:不保存容器,只要运行完毕就自动清除
镜像是容器的静态形式。
容器化的应用。
镜像和容器的操作。
资源冗余:为了保证容器运行环境的一致性,镜像必须把应用程序所在操作系统的根目录 rootfs 都包含进来,虽然容器共享了操作系统内核,但是重复的打包资源,会造成大量的冗余。
Layer:抽取重复的部分并共享,容器镜像内部由许多的镜像层组成,每层都是只读不可修改的一组文件,相同的层可以在镜像之间共享,多个层像搭积木一样堆叠,使用“Union FS 联合文件系统”技术把它们合并在一起,形成容器最终看到的文件系统。
查看镜像的分层信息:
docker inspect 容器名字|CONTAINER ID
Dockerfile:
最简单的 Dockerfile 示例:
# Dockerfile.busybox
FROM busybox # 选择基础镜像
CMD echo "hello world" # 启动容器时默认运行的命令
使用 Dockerfile 创建镜像:
docker build -f dockerfile的文件名 文件路径(构建上下文)
如果省略-f 参数:docker build 就会在当前目录下找名字是 Dockerfile 的文件。如果只有一个构建目标的话,文件直接叫“Dockerfile”是最省事的。
-t 参数:指定镜像的标签(tag),Docker 会在构建完成后自动给镜像添加名字。
FROM:构建镜像的第一条指令必须是 FROM,选择基础镜像。
COPY:将开发测试时会产生一些源码、配置等文件打包进镜像中,源文件是构建上下文路径中的。
RUN:执行任意的 Shell 命令,Dockerfile 里一条指令只能是一行,在每行的末尾使用续行符 \,命令之间也会用 && 来连接,保证在逻辑上是一行。如果命令过长,就把这些 Shell 命令集中到一个脚本文件里,用 COPY 命令拷贝进去再用 RUN 来执行。
参数化运行:ARG 创建的变量只在镜像构建过程中可见,容器运行时不可见,而 ENV 创建的变量不仅能够在构建镜像的过程中使用,在容器运行时也能够以环境变量的形式被应用程序使用。
EXPOSE:声明容器对外服务的端口号
.dockerignore:
容器镜像是由多个只读的 Layer 构成的,同一个 Layer 可以被不同的镜像共享,减少了存储和传输的成本。
Dockerfile 的编写步骤
Docker build 命令的使用
一个全面的镜像管理服务站点,所有镜像都在这里保管,如同档案馆。
如果没有明确指明镜像仓库地址, docker pull 将向 Docker Hub(默认镜像仓库)发送拉取请求。
Docker Hub 面向公众免费开放。
Docker Hub 的镜像分类:
Docker Hub 上的镜像命名规则:
应用的版本号加上操作系统
,版本号的格式为主版本号 + 次版本号 + 补丁号
,有的标签还会加上 slim(表示这个镜像的内容是经过精简的)、fat(包含较多辅助工具)在内网环境里仿造 Docker Hub。
save 命令:将镜像导出为压缩包
load 命令:从压缩包导入 Docker
docker save ngx-app:latest -o ngx.tar
docker load -i ngx.tar
-i 和-o 表示使用标准输入输出流
镜像仓库与 Docker Hub
上传镜像到 Docker Hub
离线使用 Docker
docker cp,如果源路径是宿主机那么就是把文件拷贝进容器,如果源路径是容器那么就是把文件拷贝出容器。容器内的路径表示容器id:路径
docker cp 062:/tmp/a.txt ./b.txt
docker run 命令,-v 指定宿主机路径: 容器内路径
, 即可把宿主机路径挂载到容器内的路径,让容器共享宿主机的文件。
docker run -d --rm -v /tmp:/tmp redis
Docker 的三种网络模式:
docker run --net=host
docker run --net=bridge
端口号映射需要使用 bridge 模式,并且在 docker run 启动容器时使用 -p 参数指定本机端口:容器端口
宿主机和容器拷贝文件
宿主机与容器共享文件
容器的网络模式
宿主机和容器的网络互通
私有镜像仓库的解决方案(简单): Docker Registry
docker pull registry
docker run -d -p 5000:5000 registry
打标签
docker tag nginx:alpine 127.0.0.1:5000/nginx:alpine
上传
docker push 127.0.0.1:5000/nginx:alpine
删除后再拉取
docker rmi 127.0.0.1:5000/nginx:alpine
docker pull 127.0.0.1:5000/nginx:alpine
Docker Registry 提供了 RESTful API 来查看仓库中的镜像,具体 API 查看官方文档
分别获取了镜像列表和 Nginx 镜像的标签列表:
curl 127.1:5000/v2/_catalog
curl 127.1:5000/v2/nginx/tags/list
需要使用的镜像wordpress
、mariadb
、nginx
docker pull wordpress:5
docker pull mariadb:10
docker pull nginx:alpine
配置“MARIADB_DATABASE”等几个环境变量,用 --env 参数来指定启动时的数据库、用户名和密码
docker run -d --rm \
--env MARIADB_DATABASE=db \
--env MARIADB_USER=wp \
--env MARIADB_PASSWORD=123 \
--env MARIADB_ROOT_PASSWORD=123 \
mariadb:10
查看容器的 IP 地址:
docker inspect 9ac |grep IPAddress
docker run -d --rm \
--env WORDPRESS_DB_HOST=172.17.0.2 \
--env WORDPRESS_DB_USER=wp \
--env WORDPRESS_DB_PASSWORD=123 \
--env WORDPRESS_DB_NAME=db \
wordpress:5
查看容器的 IP 地址,方便 Nginx 进行反向代理的配置,假设为172.17.0.3
。
在当前文件夹下,编写 nginx 配置文件wp.conf
:
server {
listen 80;
default_type text/html;
location / {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_pass http://172.17.0.3;
}
}
运行 Nginx:
docker run -d --rm \
-p 80:80 \
-v `pwd`/wp.conf:/etc/nginx/conf.d/default.conf \
nginx:alpine