文章目录
前言
记录本周(25/6/30~7/6)在docker训练营(https://opencamp.cn/Docker/camp/202501)关于docker与cnb的学习到的知识与相关练习。
一、Docker基础
1.Docker简介
Docker 是一个开源的容器化平台。 它利用操作系统层级的虚拟化技术(主要是 Linux 的 cgroups 和 namespaces),将应用程序及其所有依赖项(代码、运行时环境、系统工具、系统库、配置文件等)打包成一个轻量级、可移植、自给自足的软件单元,称为 容器(Container)。这个容器可以在任何支持 Docker 的环境中(无论是开发者的笔记本电脑、物理服务器、虚拟机,还是云端)以完全相同的方式快速、可靠地运行。
以下是docker的几个关键概念:
● 容器(Container):容器是 Docker 的基本部署单元。它是一个轻量级的、独立的运行时环境,包含应用程序及其相关依赖。容器利用 Linux 内核的命名空间和控制组技术,实现了隔离性和资源管理,使得应用程序在不同的容器中运行不会相互影响。
● 镜像(Image):镜像是用于创建容器的模板。它包含了一个完整的文件系统,其中包括应用程序运行所需的所有文件、依赖和配置信息。镜像是不可变的,通过 Docker 镜像可以创建多个相同的容器实例。举例:操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。
● 镜像仓库(Image Registry):镜像仓库是用于存储和分发 Docker 镜像的地方。最常用的公共镜像仓库是 Docker Hub,上面有大量的官方和社区共享的镜像。此外,还可以搭建私有的镜像仓库,用于存放自己的镜像。
● Dockerfile:Dockerfile 是一种文本文件,用于定义 Docker 镜像的构建过程。它包含了一系列的指令,用于指定基础镜像、安装软件、拷贝文件、配置环境等。通过 Dockerfile,可以自动化地构建镜像,确保镜像的一致性和可重复性。
2.Docker容器技术与虚拟技术的区别
从上图可以看出,传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,再在该系统上再运行所需应用进程。而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。所有容器共享 Host OS 内核,但通过 Linux cgroups 和 namespaces 实现进程隔离。
3.namespace – Docker容器隔离的基石
容器本身是一个被隔离的特殊进程。而Docker通过Namespace技术实现资源隔离,使得每个容器拥有独立的系统视图,仿佛运行在单独的Linux主机上。也就是说,每一个容器都有一套各自独立的namespace来隔离彼此。
Namespace是Linux内核的一项功能,用于 隔离系统资源,使得不同Namespace中的进程拥有独立的PID、Network、Mount等等。如PID Namespace用于隔离进程 ID,使容器内只能看到自己的进程。
4.Cgroup – Docker容器资源管控的核心
在 Docker 中,Cgroups(Control Groups) 的主要任务是 管理一个宿主机上所有容器的资源分配,但它的控制可以细化到 单个容器内的进程组。其功能主要包括以下几点:
● 资源限制:控制进程组使用的内存的上限,也包括文件系统和物理内存交换用的页缓存。
● 优先级分配:控制分配的CPU时间片数量、硬盘IO吞吐,也就是实际上控制了优先级。
● 资源统计:统计资源的使用量,比如CPU使用时长、内存用量等,从而计费。
● 进程控制:执行挂起、恢复进程组的操作。
5.个人使用到的部分Docker命令
(1) 镜像格式
<repository>/<image>:<tag> #镜像的一般格式
其中repository代表仓库,如果为空则默认为 Docker Hub;image代表镜像,分为 public 和 private 两种,对于 public 的镜像无需登录即可拉取,对于 private 的镜像则需要登录后才能拉取;tag标识镜像的版本,为空则默认为latest。举例:
docker.cnb.cool/looc/git-cnb:latest #/looc/git-cnb为image(镜像)
(2) 镜像相关
docker pull alipine #拉取镜像
docker images #查看镜像
dokcer search alipine #搜索镜像
#为镜像重新tag
docker tag <image_id> alpine-figlet
(3) 创建容器
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# 举例:创建并允许 Nginx 容器
docker run -d --name dc1 -p 80:80 nginx #dc1为容器名,nginx为官方镜像名
#举例:查看容器的全部文件结构
docker run alpine ls -a #ls的功能是显示当前目录下的文件和文件夹,-a为all的意思
常用参数:
–name=NAME 为容器指定名字为NAME,不使用的话系统自动为容器命名
-d: 后台运行容器并返回容器ID,也即启动守护式容器(后台运行);
-i:以交互模式运行容器,通常与 -t 同时使用;
-t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;
也即启动交互式容器(前台有伪终端,等待交互,一般连用,即-it);
-P: 随机端口映射,大写P
-p: 指定端口映射,小写p(主机端口:容器端口)
(4) 查看容器(ps)
docker ps #列出正在运行的容器
docker ps -a #显示 所有容器(包括已停止的)
docker ps -f #按条件过滤,如docker ps -f "status=running"
docker ps --format #自定义输出格式,如docker ps --format "{{.Names}}: {{.Status}}"
docker ps -a | grep nginx # 查找包含"nginx"的容器
docker ps -a --filter "exited=1" # 查看退出码为1的容器(通常出错)
docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Status}}"
docker stop $(docker ps -q) # 停止所有运行中的容器
(5) 其他容器相关命令
#启动并重新进入容器
docker start -ai<容器名>
-a:附加到容器的输出(类似-it的交互式效果)
-i:保持交互式输入
#启动容器后再附加
docker start<容器名>
docker attach<容器名>
#attach可能导致容器直接退出,因此建议使用docker exex:
docker exec -it<容器名> /bin/sh #新建一个shell会话
#手动删除已经停止的容器
docker rm <容器名>
#将现在容器提交为镜像
docker commit <容器ID> [新镜像名:标签]
二、DockerFile
1.基础概念
Dockerfile 是一个纯文本配置文件,包含了一系列构建 Docker 镜像的指令用于描述镜像的构建过程,包括基础映像、软件包安装、文件拷贝、环境变量设置等。它就像一份详细的"烹饪食谱",告诉 Docker 如何一步步创建出可运行的容器环境。
通过编写 dockerfile,可以将应用程序、环境和依赖项打包成一个独立的容器镜像,使其可以在不同的环境和平台上运行,实现应用程序的可移植性和可扩展性。
最终的构建流程是:1.编写dockerfile文件——>2.docker build 构建镜像——>3.docker run 镜像
2.相关指令
指令名称 | 作用 | 示例 |
---|---|---|
FROM | 指定基础镜像,必须是 Dockerfile 的第一条指令。定义构建过程的起点。 | FROM ubuntu:22.04 |
RUN | 在构建过程中执行命令,用于安装软件包、创建目录等操作。每一条 RUN 指令会创建一个新的镜像层。 | RUN apt-get update && apt-get install -y nginx |
COPY | 从构建上下文复制文件或目录到镜像中,保留文件属性和权限。比 ADD 更推荐使用,因 ADD 有额外功能可能造成混淆。 | COPY app.py /usr/src/app/ |
CMD | 指定容器启动时的默认执行命令(可被运行时覆盖)。一个 Dockerfile 只能有一个生效的 CMD。 | CMD ["python", "app.py"] |
EXPOSE | 声明容器运行时监听的网络端口,仅作为文档说明(实际端口映射需在运行时通过 -p 参数指定)。 | EXPOSE 80 |
WORKDIR | 设置后续指令的工作目录,相当于 cd 命令。如果目录不存在会自动创建。 | WORKDIR /app |
ENV | 设置环境变量,可在后续指令和容器运行时使用。支持键值对格式和空格分隔的多个变量。 | ENV APP_VERSION=1.0 |
ENTRYPOINT | 配置容器启动时的主执行程序(不可被运行时完全覆盖)。常与 CMD 配合使用,CMD 作为默认参数。 | ENTRYPOINT ["nginx"] |
ARG | 定义构建时的变量(构建后不再存在),可通过 --build-arg 在构建时传入值。 | ARG USER=admin |
VOLUME | 创建挂载点,用于持久化数据和共享文件。即使容器被删除,卷中的数据仍会保留。 | VOLUME /var/lib/mysql |
USER | 指定后续指令的执行用户(以及运行容器时的默认用户),用于提升安全性。 | USER nobody |
HEALTHCHECK | 定义容器健康检查命令,Docker 会定期执行以监控容器状态。 | `HEALTHCHECK --interval=30s CMD curl -f http://localhost/ |
三、已完成相关练习
1.基础exercise1
题目要求:
练习 1:基础镜像和命令使用
要求:
1. 使用 ubuntu:22.04 作为基础镜像
2. 安装 nginx 和 curl 包
3. 创建一个简单的 HTML 文件,内容为 "Hello Docker!"
4. 将 HTML 文件复制到 nginx 的默认网站目录
5. 暴露 80 端口
6. 启动 nginx 服务
提示:
- 使用 apt-get update 和 apt-get install 安装软件包
- nginx 的默认网站目录是 /var/www/html/
- 使用 CMD 或 ENTRYPOINT 启动 nginx
完成这个 Dockerfile 后,构建镜像并运行容器,访问 http://localhost:8080 应该能看到 "Hello Docker!" 页面
dockerfile代码:
FROM ubuntu:22.04
#更新并安装nginx与curl包
RUN apt-get update && \
apt-get install -y nginx curl
#创建一个html文件并复制到nginx的网站目录
RUN echo "Hello Docker!" > /var/www/html/index.html
#暴露端口80
EXPOSE 80
#启动nginx,"-g"代表Nginx的全局配置参数标志,"daemon off;"让Nginx以前台模式运行(防止容器退出)
CMD ["nginx", "-g", "daemon off;"]
2.基础exercise2
题目要求:
练习 2:Python 应用构建
要求:
1. 创建一个简单的 Python Flask 应用的 Dockerfile
2. 实现以下功能:
- 使用 python:3.11-slim 作为基础镜像
- 安装应用依赖
- 创建非 root 用户运行应用
- 配置工作目录
- 设置环境变量
- 暴露应用端口
提示:
- 使用 requirements.txt 管理依赖
- 使用 WORKDIR 设置工作目录
- 使用 USER 指令切换用户
- 使用 ENV 设置环境变量
测试命令:
docker build -t exercise2 .
docker run -d -p 5000:5000 exercise2
curl http://127.0.0.1:5000 | grep -q "Hello Docker"
dockerfile代码:
FROM python:3.11-slim
# 设置容器内环境变量
ENV FLASK_APP=app.py \
FLASK_RUN_HOST=0.0.0.0 \
PORT=5000
# 创建工作目录
WORKDIR /app
# 复制依赖文件并安装
COPY requirements.txt .
RUN pip install -r requirements.txt && \
# 创建用户组和用户
addgroup appuser && \
adduser --ingroup appuser appuser
# 复制应用代码
COPY . .
# 修改文件所有权给非 root 用户
RUN chown -R appuser:appuser /app
# 切换到非 root 用户
USER appuser
# 暴露应用端口
EXPOSE $PORT
# 设置容器启动命令
CMD ["flask", "run"]
3.基础exercise3
题目要求:
练习 3:Rust 多阶段构建
要求:
1. 使用多阶段构建来优化 Rust 应用的 Docker 镜像
2. 第一阶段:
- 使用 rust:1.75-slim 作为基础镜像
- 设置工作目录
- 复制 Cargo.toml 和 Cargo.lock(如果存在)
- 复制源代码
- 安装 MUSL 目标环境, 支持交叉编译 rustup target add x86_64-unknown-linux-musl
- 使用 cargo build --target x86_64-unknown-linux-musl --release 构建应用
3. 第二阶段:
- 使用 alpine:latest 作为基础镜像
- 从第一阶段复制编译好的二进制文件
- 设置工作目录
- 运行应用
提示:
1. 使用 COPY --from=builder 从构建阶段复制文件
2. 注意文件权限和所有权
3. 确保最终镜像尽可能小, 小于 20 M
测试命令:
docker build -t rust-exercise3 .
docker run rust-exercise3
dockerfile代码:
# 第一阶段:构建阶段
FROM rust:1.75-slim AS builder
# 设置工作目录
WORKDIR /app
# 先复制依赖声明文件;使用通配符*时,如果匹配不到不会报错只是跳过
COPY Cargo.toml Cargo.lock* ./
# 复制源代码
COPY src ./src
# 安装 MUSL 编译目标(静态链接库,Alpine 必需)
RUN rustup target add x86_64-unknown-linux-musl
# 编译应用(使用 MUSL 目标,静态链接依赖)
RUN cargo build --target x86_64-unknown-linux-musl --release
# 第二阶段:运行阶段(使用最小化 Alpine 镜像)
FROM alpine:latest
# 安装 CA 证书(某些 HTTPS 请求可能需要)
RUN apk --no-cache add ca-certificates
# 设置工作目录(二进制文件将放在这里)
WORKDIR /app
# 精确复制
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/rust-docker-example ./
# 设置容器启动命令(运行二进制文件)
CMD ["./rust-docker-example"]
总结
在本周完成了docker中基础内容的学习与练习,争取加快学习后续相关知识并完成至少一个项目。