Zumpyx's Blog

Docker 笔记

dockercontainers

安装 docker

https://docs.docker.com/engine/install/ubuntu/

一般选择在 Linux 中安装 Docker Engine,以 Ubuntu 为例。由于 Ubuntu 发行版的包管理器 APT 中提供的 Docker 包不是官方包,因此需要先卸载

sudo apt-get remove docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc

安装方式

  1. 安装 Docker Desktop 内部捆绑 Docker Engine。这是最简单的方法
  2. 使用 Docker 提供的脚本自动安装。建议用于测试环境
  3. 设置 Docker 存储库,使用软件包管理器安装。(推荐)

使用 apt 包管理器安装 Docker Engine

设置 apt 存储库

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

安装 Docker 包

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

安装完可以重启机器,或者启动 docker 守护程序

sudo systemctl start docker

验证安装情况,出现以下信息,视为安装成功

sudo docker run hello-world
---
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete 
Digest: sha256:d000bc569937abbe195e20322a0bde6b2922d805332fd6d8a68b19f524b7d21d
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

关于卸载

sudo apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras

# 删除镜像、容器、配置文件等
 sudo rm -rf /var/lib/docker
 sudo rm -rf /var/lib/containerd

常用命令

镜像可以在 https://hub.docker.com/ 中查找

简单示例

sudo docker pull [作者]/[服务]:[版本]	# 缺省作者为 docker hub 社区管理员构建,缺省版本号,则为最新稳定版本 latest
sudo docker pull nginx:latest		# 拉取社区管理员维护的 nginx 最新稳定版本镜像
docker pull pytorch/pytorch:2.2.1-cuda11.8-cudnn8-runtime	# 拉取 pytorch 维护的 2.2.1-cuda11.8-cudnn8-runtime 版本 pytorch 镜像

sudo docker images	# 查看镜像,其中 TAG 标签来区分版本
---
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
nginx         latest    e4720093a3c1   3 weeks ago     187MB
nginx         1.23      a7be6198544f   9 months ago    142MB
hello-world   latest    d2c94e258dcb   10 months ago   13.3kB

sudo docker run -d nginx:1.23	# 创建容器并运行,-d 参数表示在后台运行容器,并打印容器 ID;如果对应镜像不存在,则自动在 docker hub 搜索下载。
---
953f6a0d1ec894266a3a4cfa04b92fe9d5332e9574b8e2799aa7acebccc5fec8

sudo docker ps		# 查看正在运行的容器
---
CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS     NAMES
953f6a0d1ec8   nginx:1.23   "/docker-entrypoint.…"   9 seconds ago   Up 7 seconds   80/tcp    vigilant_mccarthy

sudo docker logs 953f6a0d1ec8	# 查看容器 ID 对应容器的日志
---
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2024/03/12 07:05:27 [notice] 1#1: using the "epoll" event method
2024/03/12 07:05:27 [notice] 1#1: nginx/1.23.4
2024/03/12 07:05:27 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6) 
2024/03/12 07:05:27 [notice] 1#1: OS: Linux 5.15.0-79-generic
2024/03/12 07:05:27 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/03/12 07:05:27 [notice] 1#1: start worker processes
2024/03/12 07:05:27 [notice] 1#1: start worker process 29

sudo docker stop 953f6a0d1ec8	# 停止容器 ID 对应的容器

端口映射

容器默认网络状态为内部私有地址,主机外部无法访问,一般使用端口映射来对外提供服务。

sudo docker run -d -p 8080:80 nginx:1.23	# -p 参数添加端口映射 [主机端口]:[容器端口]
sudo docker ps
---
CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS                                   NAMES
bd0d9c866b54   nginx:1.23   "/docker-entrypoint.…"   5 seconds ago   Up 4 seconds   0.0.0.0:8080->80/tcp, :::8080->80/tcp   heuristic_chandrasekhar

容器管理

sudo docker ps -a 	# 查看所有历史容器
---
CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS                      PORTS                                   NAMES
bd0d9c866b54   nginx:1.23    "/docker-entrypoint.…"   2 minutes ago    Up 2 minutes                0.0.0.0:8080->80/tcp, :::8080->80/tcp   heuristic_chandrasekhar
dedb77bee8e1   nginx:1.23    "/docker-entrypoint.…"   2 minutes ago    Exited (0) 2 minutes ago                                            cranky_cori
953f6a0d1ec8   nginx:1.23    "/docker-entrypoint.…"   11 minutes ago   Exited (0) 2 minutes ago                                            vigilant_mccarthy
37c0686e821b   nginx:1.23    "/docker-entrypoint.…"   13 minutes ago   Exited (0) 12 minutes ago                                           recursing_mahavira
f75bb1357697   hello-world   "/hello"                 5 hours ago      Exited (0) 5 hours ago                                              infallible_bardeen

sudo docker start f75bb1357697	# 运行容器 ID 对应的容器
# 由于容器 ID 不便于记忆,因此可以为容器设置别名(唯一),以便于运维操作
sudo docker run --name webserver -d -p 8081:80 nginx:1.23	# 使用 --name 设置容器别名
sudo docker ps
---
CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS                                   NAMES
1e102957649e   nginx:1.23   "/docker-entrypoint.…"   6 seconds ago   Up 4 seconds   0.0.0.0:8081->80/tcp, :::8081->80/tcp   webserver
bd0d9c866b54   nginx:1.23   "/docker-entrypoint.…"   7 minutes ago   Up 7 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   heuristic_chandrasekhar

# rm 为删除容器,可以使用容器 ID 也可以使用容器名称, 需要注意的是正在运行的容器无法删除
sudo docker rm f75bb1357697 37c0686e821b 953f6a0d1ec8 dedb77bee8e1 webserver	
---
f75bb1357697
37c0686e821b
953f6a0d1ec8
dedb77bee8e1
Error response from daemon: cannot remove container "/webserver": container is running: stop the container before removing or force remove

sudo docker stop webserver && sudo docker rm webserver
sudo docker ps -a
---
CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS          PORTS                                   NAMES
bd0d9c866b54   nginx:1.23   "/docker-entrypoint.…"   12 minutes ago   Up 12 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   heuristic_chandrasekhar

# 也可以构造出清空所有容器的命令,结束正在运行的容器后,删除所有容器
sudo docker stop `sudo docker ps | awk '{print $1}' | grep -v CONTAINER`; sudo docker rm `sudo docker ps -a | awk '{print $1}' | grep -v CONTAINER`

# 删除镜像的命令则为 rmi。同样,正在使用的镜像无法删除。删除镜像 ID 或者镜像全称对应的镜像。
sudo docker rmi e4720093a3c1 a7be6198544f nginx:latest

在容器内执行命令

sudo docker exec -it webserver uname -a	# exec 为执行命令,-i 是交互式接口,-t 是获得 tty,后续命令与参数空格间隔
---
Linux 23f9d480ce8e 5.15.0-79-generic #86-Ubuntu SMP Mon Jul 10 16:07:21 UTC 2023 x86_64 GNU/Linux

sudo docker exec -it webserver bash	# 也可以启动 bash shell
---
root@23f9d480ce8e:/# id
uid=0(root) gid=0(root) groups=0(root)

构建镜像 - Dockerfile

每个 docker 镜像都构建于一个基础镜像,大部分基础镜像是一个轻量级的 Linux 发行版

  • 首先需要选择一个基础镜像,并在上面部署运行依赖环境。以下为一个 python 应用封装成容器的 dockerfile

  • Python App 目录结构

tree
---
.
├── Dockerfile
├── requirements.txt
└── src
    ├── lib.py
    └── main.py
  • Dockerfile 示例文件
# 注释不要加载语句后面,会被误认为参数
# 这个镜像是基于 debian-bullseye-slim 镜像构建的,现在基于它构建一个 python app 的镜像
FROM python:3.12.2-slim-bullseye

# 复制所需文件,如果目标文件夹不存在,则自动创建。
COPY . /app/
# 如果使用 COPY src /app/,docker 会复制 src 目录下的子文件,而不是整个目录结构。

# 设置工作目录,因为需要用 python 执行 main.py,因此工作目录设为 src
WORKDIR /app/src

# 执行所需命令
RUN pip install -r ../requirements.txt

# 镜像生成的容器启动后运行的命令
CMD ["python", "main.py"]
  • 构建镜像
sudo docker build -t pyexample-app:1.0 .	# -t 设置镜像名称和版本号,最后一个参数 . 表示 Dockerfile 在当前目录
---
[+] Building 1.0s (10/10) FINISHED
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 603B
 => [internal] load metadata for docker.io/library/python:3.12.2-slim-bullseye
 => [internal] load .dockerignore
 => => transferring context: 2B
 => [1/5] FROM docker.io/library/python:3.12.2-slim-bullseye@sha256:06f5843c1ad26c2638c061eb226851381ee7825121736a062aee9c3db07d02a7
 => [internal] load build context
 => => transferring context: 119B
 => CACHED [2/5] COPY requirements.txt /app/
 => CACHED [3/5] COPY src /app/
 => CACHED [4/5] WORKDIR /app/src
 => CACHED [5/5] RUN pip install -r ../requirements.txt
 => exporting to image
 => => exporting layers
 => => writing image sha256:1420c5002066d0eba470a37d9881001e9ed1901bb6029eafe780d71160ef0935
 => => naming to docker.io/library/pyexample-app:1.0
 
sudo docker images
---
REPOSITORY      TAG       IMAGE ID       CREATED         SIZE
pyexample-app   1.0       1420c5002066   3 minutes ago   141MB
nginx           latest    e4720093a3c1   3 weeks ago     187MB
  • 运行测试
sudo docker run pyexample-app:1.0
---
[+] Good