快速入门 Docker:了解其优势与局限

Docker容器概述
Docker 官网:https://docs.docker.com/

Docker 的 github 地址:https://github.com/moby/moby

Dockerhub 官网 https://registry.hub.docker.com

一、Linux 服务器、虚拟机和容器的区别

Linux 服务器

  • 定义:Linux 服务器指的是放置在数据中心或机房中的物理机器,运行完整的 Linux 操作系统。这些服务器用于网络环境或分布式处理环境中为用户提供各种服务。
  • 类型:常见的 Linux 服务器操作系统包括 Debian 系(如 Debian、Ubuntu)、RedHat 系(如 RedHat、CentOS)、SUSE、Rocky Linux、麒麟、欧拉等。
  • 优点
    • 稳定性:基于 Linux 的服务器非常稳定,即使遇到错误也不会轻易崩溃。
    • 安全性:Linux 是防止恶意软件进入并影响系统性能的优秀选择。
    • 耐用性:可以长时间运行而不影响其性能,例如一年不关机。
    • 成本效益:大多数 Linux 发行版是免费提供的,可以自由下载并在个人电脑上安装。

虚拟机 (VM)

  • 定义:虚拟机是一种通过软件模拟出来的完整硬件系统功能的计算环境,在一个完全隔离的环境中运行,可以在一台物理计算机上模拟多台计算机的工作。
  • 常见软件:Vmware Workstation、VirtualBox、KVM、Hyper-V、Xen 等。
  • 优点
    • 经济性:相比运行单独的实体服务器,使用虚拟机可以在同一主机上运行不同的系统环境,从而降低成本。
    • 隔离性:虚拟机与主机操作系统之间相互隔离,虚拟机上的操作不会影响到主机。

容器

  • 定义:容器是一种沙盒技术,主要用于将应用程序在其内部运行,并与外界环境隔离开来。同时,这种沙盒化的应用可以方便地迁移到其他宿主机器上,而不会影响其内部应用的运行情况。可以说,容器就像是装有应用软件及其所有依赖库和配置的“箱子”。
  • 目的:确保应用能够在任何环境下以相同的方式运行,无需担心底层系统的差异。
  • 特点:提供了一种轻量级的解决方案,使得开发人员能够更专注于应用程序本身,而不是部署环境。

总结

  • Linux 服务器:提供网络服务的基础架构,适用于需要高性能和稳定性的场景。
  • 虚拟机:允许在一个物理主机上运行多个独立的操作系统实例,提供了良好的资源利用效率和隔离性。
  • 容器:专注于应用层面的封装,便于应用的迁移和部署,同时保持了高效的资源利用率和快速启动时间。

以上内容概括了 Linux 服务器、虚拟机和容器的基本概念、用途及各自的优势。

二、Docker 简介及优缺点

1. Docker 是什么?

Docker 是一个开源项目,起始于 2013 年初,最初由 dotCloud 公司内部开发,基于 Google 推出的 Go 语言实现。该项目现在由 Linux 基金会管理,并遵循 Apache 2.0 协议,在 GitHub 上维护其代码库。

Docker 提供了一个开源引擎,可以轻松为任何应用创建轻量级、可移植且自给自足的容器。开发者能够将应用程序及其所有依赖打包到一个可移植的镜像中,然后在任何支持 Docker 的机器上运行该镜像。每个容器都是完全独立的沙箱环境,确保了它们之间不会相互干扰。

Docker 的设计理念来源于集装箱的概念:就像集装箱解决了货物在运输过程中需要标准化封装的问题一样,Docker 将应用程序和其依赖封装在一个个容器内,使得这些应用可以在任何环境中一致地运行,而无需担心底层系统的差异。在这个比喻中,云计算平台就好比一艘大货轮,而 Docker 则是装载应用的集装箱。

2. Docker 的优点

  • 快速:Docker 容器的启动速度非常快,通常以秒或毫秒为单位,极大地提高了部署效率。
  • 敏捷性:Docker 提供了与虚拟机类似的灵活性,但更加经济高效,使得在裸机上部署变得简单快捷。
  • 灵活性:通过“容器化”应用和系统,无需额外安装操作系统,简化了部署流程。
  • 轻量化:在同一台服务器上可以部署大量的容器(从 100 到 1000 个不等),提高了资源利用率。
  • 成本效益:作为一个开源工具,Docker 是免费提供的,降低了软件部署的成本。

此外,Docker 分为两个版本:

  • Docker CE(社区版):面向开发者和小团队,提供丰富的功能集。
  • Docker EE(企业版):为企业用户提供高级特性和支持服务。

3. Docker 的缺点

尽管 Docker 提供了许多优势,但它也有一定的局限性:

  • 共享 Linux 内核资源:所有的 Docker 容器都共享宿主机的 Linux 内核,这意味着如果内核存在漏洞,则可能影响到所有容器的安全性和稳定性。因此,在某些高安全性要求的应用场景下,Docker 可能不是最佳选择。

总结来说,Docker 作为一种创新的容器化技术,极大地简化了应用的打包和部署过程,提供了快速、灵活和经济高效的解决方案,但在安全性和隔离性方面需要注意其潜在的限制。

三、安装 Docker

  • 主机IP:172.16.29.205
  • Rocky Linux 9.5
  • 2Gib/2vCpu

1.配置主机名:
[root@Docker-Server ~]# hostnamectl set-hostname Docker-Server && bash
2.关闭防火墙
[root@Docker-Server ~]# systemctl stop firewalld && systemctl disable firewalld

#关闭 iptables 防火墙
[root@Docker-Server ~]# dnf install iptables-services -y #安装 iptable
[root@Docker-Server ~]# systemctl stop  iptables.service  && systemctl disable iptables
[root@Docker-Server ~]# iptables -F #清空防火墙规则

#关闭 selinux
[root@Docker-Server ~]# setenforce 0
setenforce: SELinux is disabled
[root@Docker-Server ~]# sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config

3.配置时间同步
[root@Docker-Server ~]# dnf install -y chrony
Last metadata expiration check: 0:05:00 ago on Mon 07 Apr 2025 02:53:22 PM CST.
Package chrony-4.5-3.el9.x86_64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!
[root@Docker-Server ~]# systemctl start chronyd
[root@Docker-Server ~]# systemctl enable chronyd
[root@Docker-Server ~]# systemctl status chronyd

[root@Docker-Server ~]# chronyc makestep #手动同步时间
200 OK
##查看时间同步状态
[root@Docker-Server ~]# chronyc tracking
[root@Docker-Server ~]# chronyc sources
4.安装基础软件包
dnf install -y \
    tree vim wget bash-completion  bash-completion lrzsz \
    net-tools sysstat iotop iftop htop unzip nc nmap telnet bc psmisc httpd-tools \
    bind-utils nethogs expect cowsay sl wget net-tools nfs-utils lrzsz gcc gcc-c++ \
    make cmake libxml2-devel openssl-devel curl curl-devel unzip sudo  libaio-devel \
    vim ncurses-devel autoconf automake zlib-devel python-devel epel-release \
    openssh-server socat ipvsadm conntrack yum-utils
5.安装 docker-ce
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils

# Step 2: 添加软件源信息
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# Step 3: 安装Docker
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Step 4: 开启Docker服务
sudo service docker start

# 注意:
# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,您可以通过以下方式开启。同理可以开启各种测试版本等。
# vim /etc/yum.repos.d/docker-ce.repo
#   将[docker-ce-test]下方的enabled=0修改为enabled=1
#
# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# yum list docker-ce.x86_64 --showduplicates | sort -r
#   Loading mirror speeds from cached hostfile
#   Loaded plugins: branch, fastestmirror, langpacks
#   docker-ce.x86_64            17.03.1.ce-1.el7.centos            docker-ce-stable
#   docker-ce.x86_64            17.03.1.ce-1.el7.centos            @docker-ce-stable
#   docker-ce.x86_64            17.03.0.ce-1.el7.centos            docker-ce-stable
#   Available Packages
# Step2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.0.ce.1-1.el7.centos)
# sudo yum -y install docker-ce-[VERSION]

6.启动 docker 服务
[root@Docker-Server ~]# systemctl start docker && systemctl enable docker
[root@Docker-Server ~]# systemctl status docker
##查看 Docker 版本信息
[root@Docker-Server ~]# docker version
Client: Docker Engine - Community
 Version:           28.0.4
 API version:       1.48
 Go version:        go1.23.7
 Git commit:        b8034c0
 Built:             Tue Mar 25 15:08:34 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          28.0.4
  API version:      1.48 (minimum version 1.24)
  Go version:       go1.23.7
  Git commit:       6430e49
  Built:            Tue Mar 25 15:06:50 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.27
  GitCommit:        05044ec0a9a75232cad458027ca83437aae3f4da
 runc:
  Version:          1.2.5
  GitCommit:        v1.2.5-0-g59923ef
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
[root@Docker-Server ~]# 

7.开启包转发功能和修改内核参数
##内核参数修改:br_netfilter 模块用于将桥接流量转发至 iptables 链,br_netfilter 内核参数需要开启转发
[root@Docker-Server ~]# modprobe br_netfilter
[root@Docker-Server ~]# cat >> /etc/sysctl.conf <<EOF
 net.bridge.bridge-nf-call-ip6tables = 1
 net.bridge.bridge-nf-call-iptables = 1
 net.ipv4.ip_forward = 1
 EOF
[root@Docker-Server ~]# vi /etc/sysctl.conf
[root@Docker-Server ~]# sysctl -p
vm.swappiness = 0
kernel.sysrq = 1
net.ipv4.neigh.default.gc_stale_time = 120
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_announce = 2
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_slow_start_after_idle = 0
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
[root@Docker-Server ~]# cat /proc/sys/net/ipv4/ip_forward
1
##输出为 1,表示包转发功能已开启

##重启docker
[root@Docker-Server ~]# systemctl restart docker
8.配置 docker 镜像加速器##被墙解决方案
cat /etc/docker/daemon.json 
{
  "registry-mirrors": ["https://pwcj7v7a.mirror.aliyuncs.com","https://www.lmirror.top",  "https://dockerhub.icu","https://docker.chenby.cn","https://docker.1panel.live","https://docker.awsl9527.cn","https://docker.anyhub.us.kg","https://dhub.kubesre.xyz","https://docker.13140521.xyz","https://docker.m.daocloud.io"]
}
cat /etc/docker/daemon.json 

{
	    "registry-mirrors": [
	        "https://docker.m.daocloud.io",
	        "https://docker.1panel.live",
	        "https://hub.rat.dev"
	    ]
	}
8.1.重启docker
[root@Docker-Server ~]# systemctl  restart docker
8.2.拉取镜像
[root@Docker-Server ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete 
a9edb18cadd1: Pull complete 
589b7251471a: Pull complete 
186b1aaa4aa6: Pull complete 
b4df32aa5a72: Pull complete 
a0bcbecc962e: Pull complete 
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
[root@Docker-Server ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    605c77e624dd   3 years ago   141MB

四、docker的基本使用

4.1、镜像的相关操作

  • 从 dockerhub 查找镜像
[root@Docker-Server ~]# docker search contos
#目前被墙 直接访问的hub.docker.com 无法查找  docker search 功能依赖于 Docker Hub 的 API,而镜像加速源无法加速该功能
  • 下载镜像
[root@Docker-Server ~]# docker pull centos
Using default tag: latest
latest: Pulling from library/centos
a1d0c7532777: Pull complete 
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
docker.io/library/centos:latest
#### docker push 上传镜像
  • 查看本地镜像
[root@Docker-Server ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
centos       latest    5d0da3dc9764   3 years ago    231MB
##    docker image ls  简写为docker images查看镜像
##    docker images -a 查看所有镜像包含隐藏镜像.

#指定版本
只写服务名字一般下载服务的最新版本.
 nginx 下载ngx最新版本 nginx:latest
下载nginx最新稳定的版本  nginx:stable 
下载指定的版本         nginx:1.20.2 
#指定系统
nginx镜像默认的系统是Debian系统
docker pull  nginx:1.20.2-alpine 使用alpine系统更加节约空间
  • 把镜像做成离线压缩包
[root@Docker-Server ~]# docker save -o  centos-1.tar.gz centos:latest
  • 解压离线镜像包导入镜像
[root@Docker-Server ~]# docker rmi centos:latest 
Untagged: centos:latest
Untagged: centos@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Deleted: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6
Deleted: sha256:74ddd0ec08fa43d09f32636ba91a0a3053b02cb4627c35051aff89f853606b59
[root@Docker-Server ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZEB
[root@Docker-Server ~]# docker load -i centos.tar.gz 
74ddd0ec08fa: Loading layer [==================================================>]  238.6MB/238.6MB
Loaded image: centos:latest
[root@Docker-Server ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
centos       latest    5d0da3dc9764   3 years ago    231MB

  • 删除镜像
[root@Docker-Server ~]# docker rmi centos:latest 
Untagged: centos:latest
Untagged: centos@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Deleted: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6
Deleted: sha256:74ddd0ec08fa43d09f32636ba91a0a3053b02cb4627c35051aff89f853606b59
##强制删除镜像
[root@Docker-Server ~]# docker rmi -f centos:latest

###  docker image rm  ===  docker rmi
  • 给镜像打标签
[root@Server ~]# docker images
REPOSITORY   TAG             IMAGE ID       CREATED        SIZE
nginx        latest          605c77e624dd   3 years ago    141MB
nginx        1.20.2-alpine   373f8d4d4c60   3 years ago    23.2MB
[root@Server ~]# docker tag nginx:1.20.2-alpine  nginx:1.20.2-alpine-AppWeb
[root@Server ~]# docker images
REPOSITORY   TAG                    IMAGE ID       CREATED        SIZE
nginx        latest                 605c77e624dd   3 years ago    141MB
nginx        1.20.2-alpine          373f8d4d4c60   3 years ago    23.2MB
nginx        1.20.2-alpine-AppWeb   373f8d4d4c60   3 years ago    23.2MB
[root@Server ~]# 

  • 查看镜像详细信息
docker 家目录/var/lib/docker/ 
#查看 nginx:alpine镜像的信息,输出的是json格式. 
[root@Server ~]# docker image inspect nginx:1.20.2-alpine-AppWeb 
 #jq专门过滤,json形式数据
[root@Server ~]# docker image inspect nginx:1.20.2-alpine-AppWeb  |jq .[].Id
"sha256:373f8d4d4c60c0ec2ad5aefe46e4bbebfbb8e86b8cf4263f8df9730bc5d22c11"

4.2、容器相关操作

1.以交互方式启动并进入容器
[root@Docker-Server ~]# docker run --name=centos -it centos /bin/bash
[root@679fa428c9f7 /]#
[root@08b5b58510cc /]# exit
exit
#输入 exit,退出容器,退出之后容器停止,不会再前台后台运行 
[root@Docker-Server ~]# docker ps -a
CONTAINER ID   IMAGE           COMMAND       CREATED          STATUS                      PORTS     NAMES
08b5b58510cc   centos:latest   "/bin/bash"   58 seconds ago   Exited (0) 49 seconds ago             centos
[root@Docker-Server ~]# 

1.1、Docker Run 命令解析
  • 命令示例
docker run --name=centos -it centos /bin/bash
1、逐部分解析
  • docker run

    • 这是 Docker 的核心命令之一,用于创建并启动一个新的容器。
    • 如果本地没有指定的镜像(如 centos),Docker 会尝试从 Docker Hub 下载该镜像。
  • --name=centos

    • 指定新创建的容器名称为 centos
    • 容器名称必须是唯一的。如果已经存在一个名为 centos 的容器,则会报错。
    • 可以通过 docker ps -a 查看所有容器的名称。
  • -i

    • 表示保持标准输入(stdin)打开,即使没有附加到终端。
    • 这使得您可以与容器进行交互。
  • -t

    • 分配一个伪终端(pseudo-TTY),允许您在终端中与容器交互。
    • -it 组合在一起通常用于启动交互式容器。
  • centos

    • 指定要使用的镜像名称。
    • 在这里,centos 是基于 CentOS 操作系统的 Docker 镜像。
    • 如果本地没有该镜像,Docker 会自动从 Docker Hub 下载最新版本的 centos 镜像。
  • /bin/bash

    • 指定容器启动后运行的命令。
    • /bin/bash 是一个常见的 Linux 命令解释器,表示启动一个 Bash Shell 会话。
    • 容器启动后,您将进入容器的 Bash 环境,可以执行命令并与容器交互。
2、运行流程
  1. 检查镜像是否存在

    • Docker 检查本地是否有 centos 镜像。如果没有,它会从 Docker Hub 下载最新的 centos 镜像。
  2. 创建并启动容器

    • 使用 centos 镜像创建一个新的容器,并分配一个伪终端。
  3. 启动交互式 Shell

    • 容器启动后,运行 /bin/bash,进入容器的 Bash 环境。
  4. 退出容器

    • 当您在容器中输入 exit 或按下 Ctrl+D 时,Bash 会话结束,容器也会随之停止。
3、注意事项
  1. 容器生命周期

    • 容器的生命周期与其主进程相关联。在这个例子中,/bin/bash 是容器的主进程。当您退出 Bash 时,容器会自动停止。
  • 如果需要让容器后台运行,可以使用 -d 参数(detached 模式)。
  1. 重新启动已停止的容器

    • 如果容器已经停止,可以使用以下命令重新启动它:
      docker start -ai centos
      
      其中:
      • start:启动一个已存在的容器。
      • -a:附加到容器的标准输出和错误流。
  • -i:保持标准输入打开。
选项功能描述
-d容器后台运行并输出容器的ID。
-p端口映射,格式为宿主机端口:容器端口,例如80:80
-v挂载数据卷,用于将宿主机目录或文件挂载到容器内,实现数据共享和持久化存储。
-i进入交互模式,通常与-t一起使用(-it),以提供一个交互式的shell环境。
-t分配一个伪TTY终端,通常与-i配合使用。
--name为容器指定一个自定义名称(如果不设置会随机命名),方便管理容器。
    • 如果不再需要该容器,可以使用以下命令删除它:
      docker rm centos
      
  1. 查看正在运行的容器

    • 使用以下命令查看当前正在运行的容器:
      docker ps
      
  2. 查看所有容器(包括已停止的)

    • 使用以下命令查看所有容器:
      docker ps -a
      
2、已守护进程方式启动容器
[root@Docker-Server ~]# docker run -td --name centos centos:latest   #-d 在后台运行 docker

[root@Docker-Server ~]# docker ps
CONTAINER ID   IMAGE           COMMAND       CREATED              STATUS              PORTS     NAMES
a2d48fd64ea4   centos:latest   "/bin/bash"   About a minute ago   Up About a minute             centos

3、停止容器
[root@Docker-Server ~]# docker stop centos 
centos
##强制停止容器
[root@Server ~]# docker  kill centos  
centos 
[root@Server ~]# 
4、启动容器
[root@Docker-Server ~]# docker start centos 
centos
[root@Docker-Server ~]# docker ps
CONTAINER ID   IMAGE           COMMAND       CREATED        STATUS        PORTS     NAMES
a2d48fd64ea4   centos:latest   "/bin/bash"   20 hours ago   Up 1 second             centos
5、重启容器
[root@Server ~]# docker restart centos 
centos
[root@Server ~]# 

6、进入容器
[root@Docker-Server ~]# docker exec -it centos /bin/bash
[root@a2d48fd64ea4 /]# 
[root@Docker-Server ~]# docker attach centos
Docker 进入容器内部的两种方式及区别

在 Docker 中,进入容器内部主要有两种方式:docker execdocker attach。这两种方式虽然都能让您访问容器的 shell 环境,但它们的工作机制和使用场景有所不同。

1. 使用 docker exec

命令格式

docker exec -it <container_id_or_name> /bin/bash
  • -i--interactive:保持 STDIN 打开,即使没有附加也保持打开状态。
  • -t--tty:分配一个伪终端 (pseudo-TTY)。
  • <container_id_or_name>:容器的 ID 或名称。
  • /bin/bash:您希望在容器中运行的命令,在大多数情况下是启动 Bash Shell。

特点

  • 创建新的进程docker exec 在容器内部启动一个新的进程(如 /bin/bash),这意味着它不会影响容器中已经运行的主进程。
  • 灵活性:您可以执行任何命令,不仅仅是启动一个交互式 shell。例如,可以用来查看文件、修改配置等操作。
  • 不中断服务:由于是在容器内启动新进程,因此不会中断容器中正在运行的服务或应用程序。
  • 退出后容器继续运行:当您退出通过 docker exec 启动的 shell 时,容器内的其他进程和服务将继续正常运行。

适用场景

  • 当需要在不影响容器现有进程的情况下进行调试、检查日志或修改配置时。
  • 需要执行一次性任务或命令时。

2. 使用 docker attach

命令格式

docker attach <container_id_or_name>

特点

  • 连接到主进程docker attach 直接连接到容器的主进程(通常是启动容器时指定的命令)。如果您是以交互模式启动的容器(比如使用了 -it 参数并指定了 /bin/bash),那么 attach 会将您的终端直接连接到该 Bash Shell。
  • 共享标准输入输出:与容器的主进程共享标准输入、输出和错误流。这意味着如果容器停止运行,您的终端也会随之退出。
  • 容易中断服务:因为它是直接附着到容器的主进程上,所以如果您退出了这个进程(比如按下了 Ctrl+C),可能会导致整个容器停止运行。
  • 退出后可能停止容器:根据容器启动的方式,退出 docker attach 可能会导致容器停止运行,特别是当容器的主进程是前台运行的应用程序时。

适用场景

  • 主要用于开发环境或测试环境中,当您需要直接与容器的主进程互动时。
  • 不适合需要长时间后台运行的服务容器,因为意外退出可能导致容器停止。

两种方式的区别

特性docker execdocker attach
作用在容器内启动一个新的进程连接到容器的主进程
对容器的影响不影响容器中的其他进程可能会影响容器的主进程
退出后的效果容器继续运行可能导致容器停止运行
安全性更加安全,不会意外关闭容器需要小心操作,避免误操作导致容器停止
用途调试、检查、临时任务实时监控主进程输出或直接与其交互

3. 总结
  • docker exec 是更常用的方法,因为它更加灵活且不会影响容器的正常运行,适用于绝大多数情况。
  • docker attach 更适合于需要直接与容器的主进程交互的场景,但在使用时需谨慎,以免不小心停止了容器。

选择哪种方式取决于您的具体需求和操作场景:

  • 如果只是想快速检查一下容器内部的状态或执行某些临时任务,推荐使用 docker exec
  • 如果需要实时监控容器主进程的输出或者直接与其交互,则可以选择 docker attach
7、宿主机容器文件传输
#传输文件到容器
[root@Server ~]# cat index.html 
hello
[root@Server ~]# 
[root@Server ~]# docker ps
CONTAINER ID   IMAGE                        COMMAND                  CREATED              STATUS              PORTS                                 NAMES
397e6370a34c   nginx:1.20.2-alpine-AppWeb   "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:88->80/tcp, [::]:88->80/tcp   AppWeb

[root@Server ~]# docker cp index.html  AppWeb:/usr/share/nginx/html/
Successfully copied 2.05kB to AppWeb:/usr/share/nginx/html/
[root@Server ~]# curl 127.0.0.1:88
hello
#从容器中复制文件到宿主机
[root@Server ~]# ll index.html 
-rw-r--r-- 1 root root 6 Apr  9 10:48 index.html
[root@Server ~]# rm -rf index.html 
[root@Server ~]# 
[root@Server ~]# docker cp   AppWeb:/usr/share/nginx/html/index.html  /root/
Successfully copied 2.05kB to /root/
[root@Server ~]# ll /root/index.html 
-rw-r--r-- 1 root root 6 Apr  9 10:48 /root/index.html
[root@Server ~]# cat index.html 
hello
[root@Server ~]# 


8、保留对容器的修改(配置,代码)

通过镜像启动的容器,往往无法完全符合我们使用要求. 修改配置. 上传代码. 如何把修改保留下来. 把被修改的容器保存成镜像.

#1. 修改容器的服务的配置(ngx),修改页面首页文件. 上一步传输了一个index.html 文件到容器中
#2. 保留修改,生成镜像.
[root@Server ~]# docker commit AppWeb appweb_v2_index
sha256:f2706928449f4e99f5b6624d4ad88e0f789dbcf8674e3072e50b4f21a0341a34
[root@Server ~]# docker images
REPOSITORY        TAG                    IMAGE ID       CREATED         SIZE
appweb_v2_index   latest                 f2706928449f   6 seconds ago   23.2MB
nginx             latest                 605c77e624dd   3 years ago     141MB
nginx             1.20.2-alpine          373f8d4d4c60   3 years ago     23.2MB
nginx             1.20.2-alpine-AppWeb   373f8d4d4c60   3 years ago     23.2MB
centos            latest                 5d0da3dc9764   3 years ago     231MB
[root@Server ~]# 
[root@Server ~]#  docker run -d -p 8888:80  --name appweb_v2_index --restart always  appweb_v2_index:latest   #--restart always重启策略
55b080848291882f5dd1ce6b5e979d82233a38707aca630d1256dd1b00331531
[root@Server ~]# docker ps 
CONTAINER ID   IMAGE                        COMMAND                  CREATED          STATUS          PORTS                                     NAMES
55b080848291   appweb_v2_index:latest       "/docker-entrypoint.…"   4 seconds ago    Up 3 seconds    0.0.0.0:8888->80/tcp, [::]:8888->80/tcp   appweb_v2_index
397e6370a34c   nginx:1.20.2-alpine-AppWeb   "/docker-entrypoint.…"   14 minutes ago   Up 14 minutes   0.0.0.0:88->80/tcp, [::]:88->80/tcp       AppWeb
c62b80f9c55d   centos:latest                "/bin/bash"              16 minutes ago   Up 16 minutes                                             centos
[root@Server ~]# curl 127.0.0.1:8888
hello
[root@Server ~]# 

  • 通过docker commit可以实现初步自定义镜像.
  • 手动自定义镜像流程:
    • 选择合适的基础 启动容器, 镜像 .
    • 连接容器,部署,配置,调试 .
    • commit生成镜像 通过镜像创建容器并调试
9、删除容器
[root@Docker-Server ~]# docker rm -f centos 
centos

-f 强制删除

10、其他容器指令
  • 查看docker命令帮助

[root@Docker-Server ~]# docker --help

  • docker kill 用于结束指定的容器. stop无法关闭,可以使用kill.

  • docker top 查看容器中进程信息.

  • docker stats 查看所有容器的cpu,内存,磁盘,网络,进程信息.

  • 资源限制通过docker run的时候加上资源限制的选项.

#限制最大使用的内存50MB,最多使用1个cpu核心.
[root@Server ~]# docker run -d -p 85:80 --name nginx_1.20.2_limit -m 50MB --cpus 1 nginx:1.20.2-alpine
命令说明参数
docker run启动容器-d, -it, -p, --name, --restart, --cpus, --memory (-m)
docker ps查看容器信息-a, -q
docker rm删除容器-f
docker exec连接容器-it
docker attach连接容器
docker cp传输文件或目录到容器
docker commit提交,把容器保存为镜像
docker start/stop/restart/pause/unpause开/关/重启/暂停/取消暂停
docker kill结束容器
docker top/stats查看容器状态
docker inspect查看信息
docker export/save导出镜像
11、通过 docker 部署 nginx 服务
[root@Docker-Server ~]# docker run  -itd --name nginx -p 80 -itd  nginx
963142a405a89aee74af99276b64c792540d7d1d210e7402ac1b16df921b51b8
##-p 把容器端口随机在物理机随机映射一个端口
[root@Docker-Server ~]# docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                       NAMES
963142a405a8   nginx     "/docker-entrypoint.…"   3 seconds ago   Up 3 seconds   0.0.0.0:32768->80/tcp, [::]:32768->80/tcp   nginx
[root@Docker-Server ~]# curl 127.0.0.1:32768
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@Docker-Server ~]# 

12、通过宿主机访问 Docker 容器内的 Web 应用流程

假设有一个运行在 Docker 容器中的 Web 应用,容器内部监听的是 80 端口。我们使用以下命令启动容器并进行端口映射:

docker run -d --name mywebapp -p 8080:80 nginx
配置信息
  • 宿主机 IP: 192.168.1.10
  • 宿主机端口: 8080
  • 容器 IP: (容器内部 IP,通常不直接用于外部访问)
  • 容器端口: 80
具体流程
1. 客户端发起请求

客户端发送 HTTP 请求到宿主机的 IP 和映射端口:

http://192.168.1.10:8080
2. 宿主机接收请求并通过 iptables 转发

当请求到达宿主机的指定端口(如 8080),Docker 使用 iptables 规则将这些请求转发到对应的容器内的目标端口(如 80)。具体过程如下:

2.1. Docker 创建 iptables 规则

当你使用 -p 8080:80 参数启动容器时,Docker 会自动创建一系列 iptables 规则来处理端口转发。这些规则主要涉及以下几个链和表:

  • NAT 表:用于网络地址转换。
    • PREROUTING 链:用于处理进入宿主机的数据包,在路由决策之前。
    • DOCKER 链:Docker 自定义的链,包含具体的端口转发规则。
    • POSTROUTING 链:用于处理即将离开宿主机的数据包,在路由决策之后。
2.2. 示例 iptables 规则

假设你运行了上述的 docker run 命令,Docker 会在宿主机上添加类似的 iptables 规则:

# 将宿主机的 8080 端口请求转发到 Docker 的网桥接口(假设为 docker0)
sudo iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80

# 处理从容器返回的数据包,确保它们正确地被 NAT 回源地址
sudo iptables -t nat -A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 80 -j MASQUERADE

在这个例子中:

  • 172.17.0.2 是容器的实际 IP 地址。
  • --dport 8080 指定宿主机上的目标端口。
  • --to-destination 172.17.0.2:80 指定要转发到的目标容器 IP 和端口。
2.3. 数据包路径
  1. 进入宿主机:数据包首先到达宿主机的网络接口,并进入 PREROUTING 链。
  2. DNAT(目的地址转换):根据 Docker 添加的规则,数据包的目的地址和端口被修改为容器的 IP 和端口(如 172.17.0.2:80)。
  3. 路由决策:修改后的数据包被路由到容器所在的网络接口(如 docker0)。
  4. 容器处理:数据包进入容器,并由容器内的应用程序处理。
3. 容器处理请求

容器内的应用程序(如 Nginx 或其他 Web 服务器)接收到请求并进行处理。

4. 响应返回给客户端

处理后的响应需要通过相同的路径返回给客户端:

  1. 容器发出响应:响应数据包从容器发出,经过 Docker 的网络接口。
  2. MASQUERADE(源地址伪装):为了确保响应能够正确返回给客户端,Docker 使用 MASQUERADE 规则修改响应数据包的源地址为宿主机的 IP 地址。
  3. 路由回客户端:修改后的响应数据包通过宿主机的网络接口返回给客户端。
总结

为了更加清晰和准确地描述这一过程,可以表述为:

  • 客户端请求宿主机的 IP 和映射端口(如 192.168.1.10:8080)。
  • Docker 根据端口映射规则将请求通过 iptables 转发到容器内的对应端口(如容器内的 80 端口)。
    • Docker 在 iptables 的 NAT 表中添加规则,将宿主机的端口请求重定向到容器的 IP 和端口。
    • 数据包经过 DNAT 转换后被路由到容器。
  • 容器内的服务处理请求并生成响应,然后通过相同路径返回给客户端。
    • 响应数据包通过 MASQUERADE 规则伪装源地址,确保正确返回给客户端。

这种方式确保了外部用户可以通过宿主机的 IP 和端口访问到容器内的服务,而不需要知道容器的具体 IP 地址和端口配置。


示例命令
docker run -d --name mywebapp -p 8080:80 nginx

这条命令将宿主机的 8080 端口映射到容器内的 80 端口,使得外部用户可以通过访问 http://192.168.1.10:8080 来与容器内的 Web 应用进行交互。

13、端口映射
  • 1对1端口映射
参数说明示例
-p宿主机端口:容器中的端口-p 80:80
-p :容器中端口容器中端口-p :80 表示宿主机端口随机,很少用
-p 端口范围:端口范围端口范围80-88:80-88
[root@Server ~]# docker run -d  --name  nginx_ports  -p 80:80 nginx:latest 
  • 映射多个端口
##一个一个写
[root@Server ~]# docker run -d --name nginx_ports_v2  -p 8080:8080 -p 8081:8081 -p 88:80 nginx:latest 
f62411653c8e5b82762ff0a30ce4d51bcfdbf276c36eb515cb4095893785a6d9
[root@Server ~]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS                                                                                                  NAMES
f62411653c8e   nginx:latest   "/docker-entrypoint.…"   3 seconds ago        Up 3 seconds        0.0.0.0:8080-8081->8080-8081/tcp, [::]:8080-8081->8080-8081/tcp, 0.0.0.0:88->80/tcp, [::]:88->80/tcp   nginx_ports_v2

##表示连续
[root@Server ~]# docker run  -d  --name nginx_ports_v3  -p 8084-8086:8080-8082 -p 88:80 nginx:latest 
a0d9bf3bdfe929a06cbc89b803e47cc8b9e2b6d1af99c573d186ce880e4fcf7f
[root@Server ~]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                                                                                                                                                                        NAMES
a0d9bf3bdfe9   nginx:latest   "/docker-entrypoint.…"   4 seconds ago   Up 4 seconds   0.0.0.0:88->80/tcp, [::]:88->80/tcp, 0.0.0.0:8084->8080/tcp, [::]:8084->8080/tcp, 0.0.0.0:8085->8081/tcp, [::]:8085->8081/tcp, 0.0.0.0:8086->8082/tcp, [::]:8086->8082/tcp   nginx_ports_v3
[root@Server ~]# 

  • ip绑定端口

显示用户只能通过宿主机的某个网卡连接这个端口

[root@Server ~]# docker run -d -p  172.16.29.205:8080:80 --name nginx_secure nginx:latest 
392510741b009a443563757053514de7d5725cffa344d7a4588d7546f07ee7d0
[root@Server ~]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                        NAMES
392510741b00   nginx:latest   "/docker-entrypoint.…"   8 seconds ago   Up 7 seconds   172.16.29.205:8080->80/tcp   nginx_secure
[root@Server ~]# netstat -lntup |grep 8080
tcp        0      0 172.16.29.205:8080      0.0.0.0:*               LISTEN      97328/docker-proxy  
[root@Server ~]# curl 172.16.29.205:8080
14、数据卷挂载
1、概述
  • 创建了一个容器,容器里面存放很多代码,数据,软件包等等信息,不小心删除容器docker rm -f, 数据也就跟着删除了
  • 数据持久化,数据卷(挂载),让数据永久保存在宿主机中
  • 不使用数据卷,数据会丢失
  • 数据卷使用 -v选项
docker run
-v指定要挂载的目录/文件
-v 宿主机的路径:容器内部路径
2、挂载文件

挂载文件 宿主机的/data/app/index.html 挂载到容器/usr/share/nginx/html/index.html

[root@Server ~]# mkdir -p /data/app/
[root@Server ~]# echo docker data volumne >/data/app/index.html
[root@Server ~]# cat /data/app/index.html
docker data volumne
[root@Server ~]# 
###挂载
[root@Server app]# docker run -d -p 80:80 --name nginx_volumne --restart always  -v /data/app/index.html:/usr/share/nginx/html/index.html  nginx:1.20.2-alpine-AppWeb 
[root@Server app]# docker ps 
CONTAINER ID   IMAGE                        COMMAND                  CREATED          STATUS          PORTS                                 NAMES
fbd13421ec93   nginx:1.20.2-alpine-AppWeb   "/docker-entrypoint.…"   7 seconds ago    Up 6 seconds    0.0.0.0:80->80/tcp, [::]:80->80/tcp   nginx_volumne
[root@Server app]# curl 127.0.0.1:80
docker data volumne
[root@Server app]# 
15、 挂载到数据卷空间

做数据持久化,不关注数据具体放在哪里

## 创建数据卷
[root@Server app]# docker volume create  data
data
##查看数据卷
[root@Server app]# docker volume ls
DRIVER    VOLUME NAME
local     data
[root@Server app]# docker run -d  --name nginx_data -p  80:80 -v data:/var/log nginx:1.20.2-alpine-AppWeb 
638168a83390f0a00d1889e3f7fa83ef0781134d20dbc26bf21cc8723d4fcb13
[root@Server app]# docker inspect data 
[
    {
        "CreatedAt": "2025-04-09T15:00:59+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/data/_data",
        "Name": "data",
        "Options": null,
        "Scope": "local"
    }
]
[root@Server app]# ll /var/lib/docker/volumes/data/_data/nginx/
access.log  error.log
  • 查看容器挂载的数据卷
[root@Server app]# docker inspect nginx_data   |jq .[].HostConfig.Binds
[
  "data:/var/log"
]
[root@Server app]# 

五、容器架构自动化部分

1、dockerfile

1.1.Dockerfile概述

Dockerfile 是一个用来构建 Docker 镜像的文本文件,它包含了用户可以在命令行上调用的所有命令来组装一个镜像。通过 Dockerfile 可以自动化地创建 Docker 镜像的过程,这种方式不仅简化了镜像的创建过程,还确保了环境的一致性和可重复性。

1.2.基本结构和指令

Dockerfile 由一系列指令(instructions)和参数组成。每条指令在 Dockerfile 中占一行,并且顺序执行。以下是 Dockerfile 中一些常用的指令:

  1. FROM:指定基础镜像,所有的 Dockerfile 都必须以 FROM 指令作为开始。

    FROM ubuntu:latest
    
  2. LABEL:为生成的镜像添加元数据(metadata),如版本、描述等。

    LABEL version="1.0"
    
  3. RUN:用于在 Dockerfile 所构建的镜像中运行命令。主要用于安装软件包或执行某些初始化操作。

    RUN apt-get update && apt-get install -y nginx
    
  4. CMD:提供容器启动时默认要执行的命令。注意,Dockerfile 中可以有多个 RUN 指令,但只能有一个 CMD 指令。如果指定了多个 CMD 指令,则只有最后一个会生效。

    CMD ["nginx", "-g", "daemon off;"]
    
  5. EXPOSE:声明该容器内部的服务端口,供外部访问使用。但这并不意味着宿主机自动开放这个端口,需要配合 -p 或者 -P 参数使用。

    EXPOSE 80
    
  6. ENV:设置环境变量,这些环境变量可以在后续的 RUN、CMD 和 ENTRYPOINT 指令中使用。

    ENV NGINX_VERSION=1.17.10
    
  7. COPYADD:将宿主机上的文件或者目录复制到镜像中。ADD 指令相比 COPY 提供了更多的功能,例如可以从 URL 下载文件并解压缩 tar 文件。

    COPY . /usr/src/myapp
    ADD https://example.com/file.tar.gz /usr/src/myapp/
    
  8. ENTRYPOINT:配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。它可以和 CMD 一起使用,ENTRYPOINT 设置命令及参数,CMD 仅设置参数。

    ENTRYPOINT ["nginx"]
    
   
9. **WORKDIR**:指定工作目录,之后的 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令都会基于此目录。
   ```dockerfile
   WORKDIR /usr/src/myapp
  1. VOLUME:创建一个挂载点,用于从宿主机或其他容器挂载卷。
    VOLUME ["/data"]
    
1.3、构建镜像

完成 Dockerfile 编写后,可以通过以下命令构建镜像:

docker build -t friendlyname .

这里的 friendlyname 是你给镜像起的名字,. 表示当前目录下的 Dockerfile。

Dockerfile 的设计目的是为了帮助开发者更方便地打包应用及其依赖到一个轻量级、可移植的容器中,并保证应用能在任何环境中一致地运行。

1.4、Dockerfile的使用
1.准备目录及Dockerfile

[root@Server dockfile]# cat Dockerfile 
FROM centos:7
LABEL version="1.0"
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN curl -o  /etc/yum.repos.d/epel.repo  http://mirrors.aliyun.com/repo/epel-7.repo
RUN yum -y install nginx 
RUN rm -rf /usr/share/nginx/html/index.html
RUN echo  'hello Dockerfile' > /usr/share/nginx/html/index.html
CMD ["nginx","-g","daemon off;"]
[root@Server dockfile]# 
2.根据Dockerfile构建镜像
[root@Server dockfile]# docker build . -t 'nginx:centos-7'
   . 表示 Dockerfile 所在的路径,在这里,. 表示当前目录
   -t 用于指定镜像的名称和标签
   --指定自定义 Dockerfile 文件名
     如果你的 Dockerfile 文件名不是默认的 Dockerfile,而是其他名字(例如 MyDockerfile),可以通过 -f 参数指定:
[root@Server dockfile]# docker build -f MyDockerfile -t 'nginx:centos-7' . 

###如果构建过可以清除缓存后重新构建:
##不使用缓存
[root@Server dockfile]# docker build --no-cache . -t nginx:centos-7
查看详细的构建过程
如果你想查看更详细的构建日志,可以加上 --progress=plain 或 --debug 参数:

docker build --progress=plain -t 'nginx:centos-7' .
3.运行
[root@Server dockfile]# docker run -d --name nginx-centos-7 -p 80:80 nginx:centos-7
a5566919f6062848d293392a5c7feadae2a74862b1425ed0a6bb3aa6fff36cdd
[root@Server dockfile]# curl 127.0.0.1
hello Dockerfile
[root@Server dockfile]# 
1.5、Dockerfile中的指令

Dockerfile中的指令都是大写

当然,我可以帮你将这些信息整理成一个表格。以下是根据你提供的图片内容整理的表格:

Dockerfile指令含义应用建议
FROM指定基本镜像类似于 docker pull 下载镜像FROM ubuntu:20.04尽量少写ubuntu或ubuntu:latest,尽量指定具体的版本。
LABEL用于指定容器的属性信息,作者、个人联系方式(邮件)等LABEL maintainer=“Freed”推荐使用LABEL,不推荐使用下面的MAINTAINER
MAINTAINER不再使用,推荐使用LABEL个人信息--
ENV用于创建Dockerfile中使用的变量ENV Tengine_Version空格2.3.3软件版本可以创建使用变量。
RUN制作镜像过程中需要的执行命令,通常系统配置、服务配置、部署,但不能出现阻塞当前终端的命令。RUN 系统命令即可。不建议使用连续多个RUN,合并连续多个RUN。
ADD可以把指定文件或目录拷贝到容器中(指定目录),会解压压缩包,相对于当前目录。ADD restart.tar.gz空格/app/code/restart/拷贝压缩包使用。
COPY可以把指定文件或目录拷贝到容器中(指定目录),不支持自动解压,相对于当前目录。COPY nginx.conf空格/etc/nginx/nginx.conf拷贝文件或目录。
WORKDIR指定容器的默认工作目录。WORKDIR /app/code/restart
ADD restart.tar.gz空格
一般用于配合ADD,COPY需要书写容器中路径指令。
Dockerfile中使用相对路径操作容器。
VOLUME挂载数据卷。VOLUME /usr/share/nginx/html创建随机数据卷挂载容器的目的录。
未来推荐docker run的时候指定-v即可。
EXPOSE指定镜像要对外暴露的端口EXPOSE 80用于指定一个或多个容器的端口。
未来这个端口可以被-P识别。
xxxx:80
CMD用于指定容器的入口命令,入口命令可以在docker run的时候替换,==运行镜像启动容器的时候,容器默认运行的命令是什么。CMD [“命令”, “参数01”, “参数02”]
CMD [“nginx”, “-g”, “daemon off;”]
大部分都会使用CMD。
ENTRYPOINT用于指定容器的入口命令,无法被docker run替换,docker run指定的时候仅仅作为entrypoint命令的参数而已。ENTRYPOINT [“executable”, “param1”, “param2”]使用不多。

更多指令:https://docs.docker.com/engine/reference/builder

CMD和ENTRYPOINT区别
CMD和ENTRYPOINT区别共同点区别
CMD运行容器的时候默认运行CMD或ENTRYPOINT后面的命令run的时候替换。如果指定了命令内容,cmd内容就会被替换。
ENTRYPOINT运行容器的时候默认运行CMD或ENTRYPOINT后面的命令run的时候,如果指定了命令内容,entrypoint命令的参数而已。
详细解释

共同点

  • CMDENTRYPOINT 都用于指定容器启动时要执行的命令,目前大部分使用CMD即可。

区别

  • CMD:在运行容器时,默认会运行 CMDENTRYPOINT 后面的命令。如果在 docker run 命令中指定了额外的命令内容,这些内容会替换掉 CMD 中的内容。
  • ENTRYPOINT:在运行容器时,默认会运行 ENTRYPOINT 后面的命令。如果在 docker run 命令中指定了额外的命令内容,这些内容会被当作 ENTRYPOINT 命令的参数。

好的,根据你提供的图片内容,以下是整理后的表格:

Dockerfile指令CMDENTRYPOINT
共同点用于设置容器入口命令
容器启动后默认运行什么指令什么参数
用于设置容器入口命令
容器启动后默认运行什么指令什么参数
示例CMD [“命令”, “参数1”, “参数2”]ENTRYPOINT [“命令”, “参数1”, “参数2”]
区别(非同时时间)用户通过 docker run/exec 启动进入容器的时候,指定了命令。
这个命令会替代 CMD 命令和参数。
用户通过 docker run/exec 启动进入容器的时候,指定了命令。
指定的命令选项会成为 ENTRYPOINT 命令的选项。
一起区别CMD 命令后面的内容成为 ENTRYPOINT 的命令的选项
ENTRYPOINT + CMD
如有传参,会把 CMD 内容替换。
示例
  • 使用 CMD
FROM ubuntu:20.04
CMD ["echo", "hello world"]
docker run <image>
# 输出: hello world
  • 使用 ENTRYPOINT
FROM ubuntu:20.04
ENTRYPOINT ["echo", "hello world"]
docker run <image>
# 输出: hello world
  • 结合使用 CMD 和 ENTRYPOINT
FROM ubuntu:20.04
ENTRYPOINT ["echo"]
CMD ["hello world"]
docker run <image>
# 输出: hello world
  • 替换 CMD
docker run <image> "goodbye world"
# 输出: goodbye world
  • 参数传递给 ENTRYPOINT
docker run <image> "goodbye world"
# 输出: goodbye world

2.多阶段提交

多阶段构建概述

多阶段构建是 Docker 17.05 版本引入的一个新特性,它允许在 Dockerfile 中使用多个 FROM 指令来创建多个中间镜像,并最终只保留最后一个阶段的构建结果作为最终镜像。这个功能主要用于优化构建流程,减少最终生成镜像的大小,并提高构建效率。

主要优点:
  • 减少最终镜像大小:通过仅拷贝必要的文件到最终的运行环境,而不是包含整个构建工具链和依赖。
  • 增强可维护性:将构建过程与运行时环境分离,使得 Dockerfile 更加清晰易读。
  • 提高安全性:由于最终镜像中不包含构建工具和开发依赖,减少了潜在的安全风险。
多阶段构建案例讲解: Go 程序

假设我们正在开发一个简单的 Go 应用程序,并希望将其打包成 Docker 镜像。我们可以利用多阶段构建来实现这一目标。

go程序多阶段构建
[root@Server go]# mkdir -p /data/go/Dockerfile
[root@Server go]# cd cd /data/go/Dockerfile

##示例代码(main.go[root@Server Dockerfile]# cat main.go 
package main

import "fmt"

func main() {
	    fmt.Println("Hello, Docker Multi-Stage Build!")
    }
[root@Server Dockerfile]# 
###初始化 Go 模块
[root@Server Dockerfile]# go mod init my-go-app
[root@Server Dockerfile]# go mod tidy

[root@Server Dockerfile]# cat go.mod 
module my-go-app

go 1.20
[root@Server Dockerfile]# yum install -y go
[root@Server Dockerfile]# go mod tidy
Dockerfile 实现
[root@Server Dockerfile]# cat Dockerfile 
# 第一阶段:编译阶段
FROM golang:1.20 AS builder

# 设置工作目录
WORKDIR /app

# 将 Go 模块文件复制到容器中并下载依赖
COPY go.mod ./
RUN go mod download

# 将应用程序源码复制到容器中
COPY . .

# 构建应用程序
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp .

# 第二阶段:部署阶段
FROM alpine:latest

# 安装 ca-certificates 以确保 HTTPS 请求正常工作
RUN apk --no-cache add ca-certificates

# 设置工作目录
WORKDIR /root/

# 从第一阶段拷贝已编译的应用程序到当前镜像
COPY --from=builder /app/myapp .

# 指定启动命令
CMD ["./myapp"]

解释:
  1. 第一阶段(编译阶段):

    • 使用 golang:1.20 镜像作为基础镜像,用于编译我们的 Go 程序。
    • 在容器内部设置工作目录为 /app 并下载所需的 Go 模块。
    • 将应用代码复制到容器内并执行 go build 命令进行编译。
  2. 第二阶段(部署阶段):

    • 使用 alpine:latest 作为基础镜像,这是一个非常小的 Linux 发行版,适合于生产环境。
    • 安装 ca-certificates 包以支持 HTTPS 请求。
    • 将第一阶段构建出的可执行文件复制到该镜像中。
    • 设置 CMD 指令指定容器启动时运行的命令。
构建镜像

使用以下命令来构建你的 Docker 镜像:

docker build -t my-go-app .

这将会根据 Dockerfile 中定义的步骤构建镜像,并且只有第二阶段的内容会被包含在最终的镜像里,从而保证了镜像体积尽可能小。

运行容器

构建完成后,可以通过以下命令运行容器:

docker run --rm my-go-app

能看到输出 Hello, Docker Multi-Stage Build!,这意味着你的多阶段构建成功地创建了一个精简的、只包含必要组件的 Docker 镜像。


明白了!以下是按照你的要求调整后的文档,使用 Markdown 格式,并将标题层级调整为 h4h5


多阶段构建案例讲解:Java + Spring Boot
1. 项目结构
/data/java
├── Dockerfile
├── pom.xml
├── settings.xml
└── src
    └── main
        └── java
            └── com
                └── example
                    └── Main.java
  • Main.java:Spring Boot 的主类。
  • pom.xml:Maven 配置文件,用于管理依赖项。
  • Dockerfile:多阶段构建配置。
2. 示例代码
2.1 Java 源代码 (src/Main.java)

mkdir -p /data/java/src/main/java/com/example

cd /data/java/

[root@Server java]# cat src/main/java/com/example/Main.java
    
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

    @GetMapping("/")
    public String home() {
        return "Hello, Docker Multi-Stage Build with Java Web!";
    }
}

2.2 Maven 配置文件 (pom.xml)

添加 Spring Boot 相关依赖:

[root@Server java]# cat pom.xml 
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>java-web-docker-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.example.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
3. Dockerfile

以下是完整的 Dockerfile,使用多阶段构建来编译和运行 Java Web 程序:

[root@Server java]# cat Dockerfile 
# 第一阶段:编译阶段AS  别名
FROM maven:latest AS builder

# 设置工作目录
WORKDIR /app

# 复制自定义 settings.xml 文件
COPY settings.xml /root/.m2/settings.xml

# 复制 Maven 配置文件和源码
COPY pom.xml .
COPY src ./src

# 编译并打包应用程序
RUN mvn clean package -DskipTests

# 第二阶段:运行阶段
FROM openjdk:17-jdk-alpine

# 设置工作目录
WORKDIR /app

# 从第一阶段拷贝编译好的 JAR 文件到当前镜像
COPY --from=builder /app/target/java-web-docker-demo-1.0-SNAPSHOT.jar ./app.jar

# 暴露端口
EXPOSE 8080

# 启动命令
CMD ["java", "-jar", "app.jar"]

4. 构建和运行步骤
4.1 构建镜像
  • 运行前更改 maven源加速:
[root@Server java]# cat settings.xml 
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
    <mirrors>
        <!-- 阿里云 Maven 镜像 -->
        <mirror>
            <id>aliyun</id>
            <name>Aliyun Maven</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <mirrorOf>*</mirrorOf>
        </mirror>
    </mirrors>
</settings>

/data/java 目录下执行以下命令来构建 Docker 镜像:

docker build  -t  java-web-app .
4.2 运行容器

构建完成后,可以通过以下命令运行容器,并将主机的 8080 端口映射到容器的 8080 端口:

[root@Server java]# docker run --rm   -d --name java-web-app -p 8080:8080 java-web-app
[root@Server java]# docker ps
CONTAINER ID   IMAGE          COMMAND               CREATED         STATUS         PORTS                                         NAMES
1bdf82e302d3   java-web-app   "java -jar app.jar"   3 seconds ago   Up 2 seconds   0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp   java-web-app
[root@Server java]# docker logs  java-web-app 

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.0)

2025-04-10T07:04:13.274Z  INFO 1 --- [           main] com.example.Main                         : Starting Main v1.0-SNAPSHOT using Java 17-ea with PID 1 (/app/app.jar started by root in /app)
2025-04-10T07:04:13.305Z  INFO 1 --- [           main] com.example.Main                         : No active profile set, falling back to 1 default profile: "default"


###看到这个就已经运行起来了
4.3 访问应用

打开浏览器或使用 curl 命令访问:

curl http://localhost:8080

输出结果应为:

Hello, Docker Multi-Stage Build with Java Web!
5. 验证镜像大小

为了验证多阶段构建的效果,可以查看最终生成的镜像大小:

docker images | grep java-web-app

输出示例:

[root@Server java]# docker images | grep java-web-app
java-web-app      latest                 725fd5ecd5a1   5 minutes ago   344MB
[root@Server java]# docker images |grep maven
maven             latest                 59fcf607b512   3 years ago     789MB
[root@Server java]# 

可以看到,最终镜像非常小,因为它只包含了运行时所需的 JAR 文件和 OpenJDK。

6. 总结

通过多阶段构建,我们实现了以下目标:

  1. 分离编译与运行时环境:编译阶段使用了较大的 Maven 镜像,而运行时阶段使用了轻量级的 OpenJDK 镜像。
  2. 减少镜像大小:最终镜像只包含运行应用程序所需的文件(如 JAR 文件和 JVM),去除了不必要的编译工具和依赖。
  3. 提高安全性:最终镜像中不包含任何源码或构建工具,降低了潜在的安全风险。
  4. 支持 Web 应用:通过嵌入式 Web 服务器(如 Spring Boot)提供 HTTP 服务,方便通过浏览器访问。

这样就可以通过访问 http://localhost:8080 来查看输出内容了!


3.Docker Compose

1. 什么是 Docker Compose?

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。通过使用 Compose,你可以使用 YAML 文件来配置应用程序的服务。然后,通过一个命令,你就可以从配置中创建并启动所有服务。

主要特点:

  • 单文件配置:使用 docker-compose.yml 文件定义服务。
  • 简单命令:只需一个命令即可构建、启动、停止和删除容器。
  • 可重复性:可以轻松地在不同的环境中(如开发、测试、生产)复制相同的环境。
  • 网络隔离:自动为服务创建网络,使它们能够相互通信。
2 .compose极速上手指南
  • Docker Compose 的安装
###下载慢的试下Windows下载是否快一点,下载完上传
wget https://github.com/docker/compose/releases/download/v2.34.0/docker-compose-linux-x86_64
mv docker-compose-linux-x86_64.0  /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
  • 检查安装是否成功:
docker-compose --version
2.1、文件格式
cat  docker-compose.yml 
version: "3.3"
 services:
  web_ngx:
    image: "nginx:1.20.2-alpine"
    links:
      - web_php
    ports:
      - "8000:80"
  web_php:
    image: "php:7-fpm"
 2. 启动命令   
 docker-compose up -d
2.2、docker-compose命令格式
docker-compose命令格式描述
容器
up -d创建并运行容器,类似于 docker run -d,启动的时候后台运行
down删除容器,删除所有内容(网络、数据卷)
stop/start/restartdocker container 关闭、开启、重启容器
ps查看容器运行情况,只有 -q 选项
top容器进程信息
logs容器日志
rm删除容器(需要容器已经关闭)
镜像
images查看镜像
2.3、docker-compose.yml文件配置常用字段及说明
字段说明
build指定 Dockerfile 文件名(要指定的 Dockerfile 文件需要在 build 标签的子级标签中用 dockerfile 标签指定)
dockerfile构建镜像上下文路径
context可以是 dockerfile 路径,或者时执行 git 仓库的 url 地址
images指定镜像(已存在)
command执行命令,会覆盖容器启动后默认执行的命令(会覆盖 dockerfile 中的 CMD 指令)
container_name指定容器名称,由于容器名称是唯一的,如果指定自定义名称,则无法 scale 指定容器数量。
deploy指定部署和运行服务相关配置,只能在 swarm 模式使用
environment添加环境变量
networks加入网络,引用顶级 networks 下条目
network_mode设置容器的网络模式
ports显示容器端口,与 -p 相同,但是端口不能低于 60
volumes挂载一个宿主机目录或命令卷到容器,命令卷要在顶级 volumes 定义卷名称
volumes_from从另一个服务或容器挂载卷,可选参数:ro 和 rw(仅版本 2 支持)
hostname在容器内设置内核参数
links连接到另一个容器,- 服务名称 [ ]
privileged用来给容器 root 权限,注意是不安全的,true
restart重启策略,定义是否重启容器 1、no,默认策略,在容器退出时不重启容器 2、on-failure,在容器非正常退出时(退出状态非 0),才会重启容器 3、on-failure:3 在容器非正常退出时,重启容器,最多重启 3 次 4、always,在容器退出时总是重启容器,5、unless-stopped,在容器退出时总是重启容器,但是不考虑在 Docker 守护进程启动时就已经停止了的容器。
depends_on此标签用于解决容器的依赖,启动先后问题。如启动应用容器,需要先启动数据库容器。php.depends_on - apache - mysql

3.:Nginx + PHP-FPM 多容器应用
  • 场景描述

我们将构建一个简单的 Web 应用程序,包含以下组件:

  • Nginx:作为 Web 服务器,处理 HTTP 请求。
  • PHP-FPM:作为后端服务,处理 PHP 脚本。

通过 Docker Compose,我们可以轻松地将这两个服务连接起来,并让它们协同工作。


  • 步骤 1:编写 docker-compose.yml 文件
version: "3.3"
services:
  web_ngx:
    image: "nginx:1.20.2-alpine"  # 使用官方 Nginx 镜像
    ports:
      - "8000:80"  # 将主机的 8000 端口映射到容器的 80 端口
    links:
      - web_php  # 链接到 PHP 容器
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf  # 自定义 Nginx 配置文件

  web_php:
    image: "php:7-fpm"  # 使用官方 PHP-FPM 镜像
    volumes:
      - ./www:/var/www/html  # 将本地目录挂载到容器内的 PHP 工作目录

  • 步骤 2:创建必要的文件
  1. Nginx 配置文件 (nginx.conf)
    创建一个自定义的 Nginx 配置文件,用于将请求转发到 PHP-FPM:

    server {
        listen 80;
        server_name localhost;
    
        root /var/www/html;
        index index.php;
    
        location / {
            try_files $uri $uri/ =404;
        }
    
        location ~ \.php$ {
            include fastcgi_params;
            fastcgi_pass web_php:9000;  # 将请求转发到 PHP-FPM 容器
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        }
    }
    
  2. PHP 测试文件 (www/index.php)
    创建一个简单的 PHP 文件,放置在 www 目录中:

    <?php
    echo "Hello, Docker Compose!";
    phpinfo();
    ?>
    

  • 步骤 3:启动服务

运行以下命令启动服务:

docker-compose up -d

  • 步骤 4:访问服务

打开浏览器,访问 http://<服务器IP>:8000,你应该能看到 PHP 输出的 “Hello, Docker Compose!” 和 phpinfo() 页面。


  • 步骤 5:清理环境

如果你想停止并删除所有容器和网络,可以运行以下命令:

docker-compose down

3. 使用 Dockerfile 构建自定义镜像
  • 场景描述

我们将构建一个更复杂的 Web 应用程序,包含以下组件:

  • 自定义 Nginx 镜像:使用 Dockerfile 构建一个包含自定义配置的 Nginx 镜像。
  • Node.js 后端服务:使用官方 Node.js 镜像运行一个简单的 Express 应用程序。
  • Redis 缓存:作为后端缓存服务。

  • 步骤 1:项目结构

创建以下目录结构:

myapp/
├── docker-compose.yml
├── nginx/
│   ├── Dockerfile
│   └── nginx.conf
├── backend/
│   ├── package.json
│   └── app.js

  • 步骤 2:编写 docker-compose.yml 文件
version: "3.3"
services:
  nginx:
    build:
      context: ./nginx  # 使用 Dockerfile 构建自定义 Nginx 镜像
    ports:
      - "8080:80"
    depends_on:
      - backend

  backend:
    image: "node:16-alpine"
    working_dir: /app
    volumes:
      - ./backend:/app
    command: "npm start"
    environment:
      REDIS_HOST: redis
    depends_on:
      - redis

  redis:
    image: "redis:alpine"

  • 步骤 3:创建必要的文件
  1. Nginx 的 Dockerfile

    nginx/Dockerfile 中定义自定义镜像:

    FROM nginx:1.20.2-alpine
    COPY nginx.conf /etc/nginx/nginx.conf
    
  2. Nginx 配置文件 (nginx/nginx.conf)

    server {
        listen 80;
        server_name localhost;
    
        location / {
            proxy_pass http://backend:3000;  # 将请求转发到 Node.js 服务
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    
  3. Node.js 应用程序

    • backend/package.json

      {
        "name": "myapp",
        "version": "1.0.0",
        "scripts": {
          "start": "node app.js"
        },
        "dependencies": {
          "express": "^4.17.1",
          "redis": "^4.0.0"
        }
      }
      
    • backend/app.js

      const express = require("express");
      const redis = require("redis");
      
      const app = express();
      const client = redis.createClient({
        host: process.env.REDIS_HOST || "localhost",
      });
      
      app.get("/", (req, res) => {
        client.incr("counter", (err, counter) => {
          if (err) throw err;
          res.send(`Page views: ${counter}`);
        });
      });
      
      app.listen(3000, () => {
        console.log("Server is running on port 3000");
      });
      

  • 步骤 4:启动服务

运行以下命令安装依赖并启动服务:

cd myapp
docker-compose up -d

  • 步骤 5:访问服务

打开浏览器,访问 http://<服务器IP>:8080,你应该能看到页面访问计数器。


  • 步骤 6:清理环境

运行以下命令停止并删除所有容器和网络:

docker-compose down

4.使用docker-compose运行jenkins

  • docker-compose.yml
version: "3.8"
services:
  jenkins:
    image: jenkins/jenkins:2.505-jdk17
    container_name: jenkins
    ports:
      - "8081:8080"
      - "50000:50000"
    volumes:
      - /var/jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 4g
    security_opt:
      - no-new-privileges
    logging:
      driver: "local"
      options:
        max-size: "100m"

chown -R 1000:1000 /var/jenkins_home

chmod -R 777 /var/jenkins_home


4.1关键点说明
(1) 版本声明
  • version: "3.8"
    • 指定 Docker Compose 文件的版本。确保你的 Docker 和 Docker Compose 支持该版本。
(2) 服务定义
  • jenkins 是服务的名称,对应于容器的名称。
  • image: jenkins/jenkins:2.505-jdk17
    • 指定使用的镜像及其版本。
(3) 端口映射
  • ports
    • - "8081:8080":将宿主机的 8081 端口映射到容器的 8080 端口(Jenkins Web 界面)。
    • - "50000:50000":将宿主机的 50000 端口映射到容器的 50000 端口(用于 Jenkins 的分布式构建)。
(4) 数据卷挂载
  • volumes
    • /var/jenkins_home:/var/jenkins_home:将 Jenkins 的数据目录挂载到宿主机,以实现数据持久化。
    • /var/run/docker.sock:/var/run/docker.sock:允许 Jenkins 容器使用宿主机的 Docker 服务。
(5) 重启策略
  • restart: unless-stopped
    • 设置容器在意外停止时自动重启,除非手动停止。
(6) 资源限制
  • deploy.resources.limits
    • cpus: "2":限制容器最多使用 2 核 CPU。
    • memory: 4g:限制容器的最大内存为 4GB。
(7) 安全选项
  • security_opt
    • no-new-privileges:禁止容器内的进程获取新的特权,增强安全性。
(8) 日志管理
  • logging
    • driver: "local":设置日志驱动为 local
    • max-size: "100m":限制单个日志文件的最大大小为 100MB。
4.2如何使用
  1. 创建 docker-compose.yml 文件
  • 将上述内容保存为 docker-compose.yml 文件。
  1. 启动 Jenkins 容器
    • 在包含 docker-compose.yml 文件的目录下运行以下命令:
      docker-compose up -d
      
  2. 验证容器状态
docker-compose ps
  1. 查看容器日志
    • 查看 Jenkins 容器的日志输出:
      docker-compose logs jenkins
      
  2. 访问 Jenkins Web 界面

  - 打开浏览器,访问 `http://<your-server-ip>:8081`。
  - 初次启动时,Jenkins 会提示输入管理员密码。你可以通过以下命令获取初始密码:
   cat /var/jenkins_home/secrets/initialAdminPassword
总结
  1. Docker Compose 的基本功能:如何定义多容器服务并通过 docker-compose.yml 文件管理它们。
  2. 结合 Dockerfile 的实际应用:如何使用自定义镜像与现有服务协同工作。
  3. 实际开发场景:如何将 Docker Compose 应用于真实的 Web 开发环境。
  • 注:容器编排使用k8s更强大

六、docker 容器的互联与docker网络

七、docker 的底层原理

Containerd 容器概述

在 2016 年 12 月 14 日,Docker 公司宣布将 Containerd 从 Docker 中分离,由开源社区独立发展和运营。
Containerd 是一个独立运行的容器管理工具,主要职责包括镜像管理和容器执行。它通过 containerd-shim 接口封装层与 runC 项目对接,从而使得容器引擎(如 Docker Daemon)可以独立升级。

Containerd 可以在宿主机中管理完整的容器生命周期,容器镜像的传输和存储、容器的执行和管

理、存储和网络等包括以下功能:

  • 管理容器的生命周期(从创建容器到销毁容器)
  • 拉取/推送容器镜像
  • 存储管理(管理镜像及容器数据的存储)
  • 调用 runC 运行容器(与 runC 等容器运行时交互)
  • 管理容器网络接口及网络

ctr: Containerd 的命令行客户端。


1. Containerd 和 Docker 之间的关系

  • Docker 包含 Containerd:

    • Containerd 专注于运行时的容器管理。
    • Docker 除了容器管理之外,还支持镜像构建等功能。
  • Containerd 提供的 API 偏底层:

    • 不是直接面向普通用户的,而是为容器编排系统的开发者设计的。

2. Containerd 在容器生态中的角色

  • Containerd 并不是直接面向最终用户的,而是主要用于集成到更上层的系统中,例如 Kubernetes 等容器编排系统。
  • Containerd 以 daemon 形式运行在系统上,通过 Unix Domain Socket 暴露底层的 gRPC API,供上层系统调用以管理机器上的容器。

K8S 为什么要放弃使用 Docker 作为容器运行时?

在 Kubernetes 中运行容器时,会调用容器运行时(如 ContainerdCRI-O 等),这些运行时遵循 OCI 规范,并通过 runc 实现与操作系统内核交互来完成容器的创建和运行。

Docker,Kubernetes 等工具来运行一个容器时会调用容器运行时(CRI),比如 containerd,CRI-O,通过容器运行时来完成容器的创建、运行、销毁等实际工作,Docker 使用的是 containerd 作为其运行时;Kubernetes 支持 docker(在 k8s1.24 版本之前用,1.24 开始废弃了)、containerd,CRI-O 等多种容器运行时,这些容器运行时都遵循了 OCI 规范,并通过 runc 来实现与操作系统内核交互来完成容器的创建和运行

CRI
  • CRI(Container Runtime Interface)是一个插件接口,使 kubelet 能够使用各种容器运行时。
  • 每个节点上需要有一个正常工作的容器运行时,以便 kubelet 启动 Pod 及其容器。
OCI
  • OCI(Open Container Initiative)是一个轻量级、开放的治理结构,在 Linux 基金会的支持下成立,致力于围绕容器格式和运行时创建开放的行业标准。
  • OCI 项目由 Docker、CoreOS(后被 Red Hat 收购)和容器行业的其他领导者于 2015 年 6 月启动。

如果你使用 Docker 作为 K8S 容器运行时的话,kubelet 需要先要通过 dockershim 去调用 Docker,再通过 Docker 去调用 containerd。如果你使用 containerd 作为 K8S 容器运行时的话, kubelet 可以直接调用 containerd。使用 containerd 不仅性能提高了(调用链变短了),而且资源占用也会变小(Docker 不是一个纯粹的容器运行时,具有大量其他功能)。


调用链对比

Docker 作为 K8S 容器运行时的调用关系

kubelet --> docker shim (在 kubelet 进程中) --> dockerd --> containerd

Containerd 作为 K8S 容器运行时的调用关系

kubelet --> cri plugin(在 containerd 进程中) --> containerd

为什么选择 Containerd?
  1. 性能提升:调用链变短,减少了中间环节。
  2. 资源占用降低:Docker 不是一个纯粹的容器运行时,具有大量其他功能,而 Containerd 更加轻量化。

总结

Containerd 是一个高效、轻量化的容器运行时,专为容器编排系统(如 Kubernetes)设计。相比 Docker,它提供了更直接的调用方式和更低的资源开销,因此成为 Kubernetes 默认推荐的容器运行时。

3、containerd 安装和配置

安装 docker 会自动把 containerd 安装出来,也可以通过如下文档直接安装 containerd。

3.1、配置docker-ce.repo yum源下载containerd并启动

[root@Docker-Server ~]# yum install yum-utils -y
[root@Docker-Server ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
Adding repo from: http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@Docker-Server ~]# yum install containerd -y
[root@Docker-Server ~]# systemctl enable  containerd.service 
[root@Docker-Server ~]# systemctl start  containerd.service

3.2、初始化 Containerd 配置:

[root@Docker-Server ~]# containerd config default > /etc/containerd/config.toml
###替换 containerd 默认的 sand_box 镜像
[root@Docker-Server ~]# sed -i 's#k8s.gcr.io/pause:3.2#registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.2#g' /etc/containerd/config.toml 
##配置并重新运行 containerd 服务
[root@Docker-Server ~]# systemctl daemon-reload 
[root@Docker-Server ~]# systemctl restart  containerd
[root@Docker-Server ~]# systemctl status  containerd

[root@Docker-Server ~]#  ctr images pull  registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.2
registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.2:              resolved       |++++++++++++++++++++++++++++++++++++++| 
manifest-sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108: done           |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:c74f8866df097496217c9f15efe8f8d3db05d19d678a02d01cc7eaed520bb136:    done           |++++++++++++++++++++++++++++++++++++++| 
config-sha256:80d28bedfe5dec59da9ebf8e6260224ac9008ab5c11dbbe16ee3ba3e4439ac2c:   done           |++++++++++++++++++++++++++++++++++++++| 
elapsed: 1.3 s                                                                    total:  1.3 Ki (987.0 B/s)                                       
unpacking linux/amd64 sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108...
done: 55.643964ms	
[root@Docker-Server ~]# 


4、containerd 容器相关操作

1、查看 containerd 命名空间

[root@Docker-Server ~]# ctr namespace ls
NAME    LABELS 
default        
moby           
[root@Docker-Server ~]# ctr ns ls
NAME    LABELS 
default        
moby           
[root@Docker-Server ~]# 

ctr 有命名空间 namespace 来指定类似于工作空间的隔离区域。使用方法 ctr -n default images ls来查看 default 命名空间的镜像,不加 -n 参数,默认也是使用 default 的命名空间。

1. 查看镜像列表

  [root@Docker-Server ~]# ctr image ls
REF                                                                 TYPE                                                 DIGEST                                                                  SIZE      PLATFORMS   LABELS 
registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.2 application/vnd.docker.distribution.manifest.v2+json sha256:4a1c4b21597c1b4415bdbecb28a3296c6b5e23ca4f9feeb599860a1dac6a0108 290.8 KiB linux/amd64 -      
[root@Docker-Server ~]# 
###查看moby命名空间下的镜像
[root@Docker-Server ~]# ctr -n=moby images ls
REF TYPE DIGEST SIZE PLATFORMS LABELS 
[root@Docker-Server ~]# 


3、拉取镜像

  • 拉取 busybox 镜像
[root@Docker-Server ~]# ctr image pull registry.cn-hangzhou.aliyuncs.com/google_containers/busybox:latest
registry.cn-hangzhou.aliyuncs.com/google_containers/busybox:latest:            resolved       |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:138cfc514ce4b3f1f8d57b2f9766fcb5ffab791110bcd8610e8d762cc78d28b2: done           |++++++++++++++++++++++++++++++++++++++| 
layer-sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4: done           |++++++++++++++++++++++++++++++++++++++| 
elapsed: 1.4 s                                                                 total:  32.0 B (22.0 B/s)                                        
unpacking linux/amd64 sha256:a03634868d3ef4d750ba519ffca31900ae1ed286926fb4e91cf7956223dec826...
done: 110.347915ms	
[root@Docker-Server ~]# 

2. 运行容器

要基于现有的镜像运行一个容器,可以使用 ctr run 命令。以下是一个基本的例子:

ctr run --rm --runtime io.containerd.runtime.v1.linux registry.cn-hangzhou.aliyuncs.com/google_containers/busybox:latest my-busybox-container
  • --rm: 容器退出后自动删除。
  • --runtime: 指定使用的运行时(通常为 io.containerd.runtime.v1.linux)。
  • registry.cn-hangzhou.aliyuncs.com/google_containers/busybox:latest: 镜像名称和标签。
  • my-busybox-container: 容器的名称。

3. 查看正在运行的容器

使用 ctr task ls 命令来列出所有正在运行的容器:

ctr task ls

如果容器正在运行,您将看到类似如下的输出:

TASK                    PID       STATUS    
my-busybox-container      12345     RUNNING   

4. 停止容器

如果您需要停止某个容器,可以使用 ctr task kill 命令。例如,要停止名为 my-busybox-container 的容器:

ctr task kill my-busybox-container

5. 删除容器

容器停止后,可以使用 ctr containers delete 命令删除它。首先,您可能需要获取容器的 ID 或名称:

ctr containers list

然后,使用容器的 ID 或名称删除它:

ctr containers delete <container-id-or-name>

6. 清理未使用的镜像

有时候,您可能想要清理不再使用的镜像以释放空间。可以使用 ctr image rm 命令来删除特定的镜像:

ctr image rm registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.2

或者,您可以使用 docker system prune 类似的功能来清理所有未使用的镜像、容器和其他资源,但请注意,这会删除所有未被使用的资源:

ctr images prune

7、压缩镜像导出导入镜像

##压缩镜像
[root@Docker-Server ~]# ctr images pull docker.io/library/mysql:latest
[root@Docker-Server ~]# ctr images export mysql.tar.gz docker.io/library/mysql:latest
[root@Docker-Server ~]# 
##导出镜像
[root@Docker-Server ~]# # ctr images import mysql.tar.gz
##导入镜像
[root@Server ~]# ctr image import nginx.tar.gz

注意事项

  • 权限问题:确保您以 root 用户或具有适当权限的用户身份运行这些命令。
  • 网络配置:如果在 Kubernetes 环境中使用 containerd,确保您的网络配置允许从指定的镜像仓库拉取镜像。
  • 日志和调试:对于生产环境中的部署,建议检查 containerd 和相关容器的日志文件,以便于排查可能出现的问题。

从您的操作来看,您已经成功使用 docker save 命令将 nginx:latest 镜像保存为 nginx.tar.gz 文件。接下来,您想了解如何将该镜像导入到 containerd 中,以及 Docker 和 containerd 的镜像是否通用。


8. Docker 和 containerd 的镜像是否通用?

答案:是的,Docker 和 containerd 的镜像格式是兼容的。

  • 原因

    • Docker 和 containerd 都遵循 OCI(Open Container Initiative)标准。
    • docker save 导出的镜像是一个符合 OCI 标准的 tar 文件,containerd 可以直接加载这种格式的镜像。
  • 注意事项

    • 虽然镜像格式兼容,但两者的存储路径和管理方式不同:
      • Docker 使用自己的存储路径(如 /var/lib/docker)。
      • containerd 使用其自身的存储路径(如 /var/lib/containerd)。
    • 因此,即使镜像可以互用,实际运行容器时需要分别使用 dockerctr 命令进行管理。

1. 如何将 Docker 导出的镜像导入到 containerd 中?
[root@Server ~]# docker save -o nginx.tar.gz nginx:latest
[root@Server ~]# ll nginx.tar.gz 
-rw------- 1 root root 145902080 Apr  8 18:22 nginx.tar.gz

以下是将 nginx.tar.gz 导入到 containerd 的步骤:

步骤 1:使用 ctr 导入镜像

使用 ctr 工具将镜像导入到 containerd 中:

[root@Server ~]# ctr image import nginx.tar.gz
步骤 2:验证镜像是否导入成功

运行以下命令查看已导入的镜像:

[root@Server ~]# ctr image ls |grep nginx
containerd v2.0. Use the `io.containerd.runc.v2` runtime instead. 
docker.io/library/nginx:latest                                      application/vnd.oci.image.manifest.v1+json           sha256:112b2cdc904bf7befbfe50544c954ac4f6996b70f7ff9e759525802a7d27834e 139.1 MiB linux/amd64 -

总结
  • Docker 和 containerd 的镜像格式是兼容的,可以通过 docker savectr image import 实现镜像的迁移。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值