侧边栏壁纸
博主头像
路小飞博主等级

行动起来,活在当下

  • 累计撰写 72 篇文章
  • 累计创建 12 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

00- ContainerRuntime - Docker

路小飞
2024-07-07 / 0 评论 / 0 点赞 / 17 阅读 / 8317 字

1.1 容器基础知识

1.1.1 Docker 介绍

Docker 是基于 Go语言实现的开源容器项目,开发者可以打包自己的应用到容器里面,然后迁移到其他机器的 Docker 应用中,可以实现快速部署。

1. 容器与虚拟机的区别

容器虚拟化的是操作系统而不是硬件,容器之间是共享同一套操作系统资源的。虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统。因此容器的隔离级别会稍低一些,但是够用。

下面是容器与虚拟机的特性对比表,更清晰地展示两者之间的差异:

特性容器虚拟机
启动秒级分钟级
硬盘使用一般为 MB一般为 GB
性能接近原生通常较弱
系统支持量单机支持上千个容器一般支持几十个
2. Docker 三要素

**Image(镜像):**是一个只读模板,含创建 Docker 容器的说明,它与操作系统的安装光盘很相似。

**Container(容器):**镜像的运行实例,镜像与容器的关系类比面向对象中的类和对象。

**Registry(仓库):**是一个集中存储与分发镜像的服务,最常用的 Registry 是官方的 Docker HUb。

3. Docker 架构

Docker架构.jpg

1.1.2 Docker 基本实现原理

主要基于内核的三项技术:

  • 资源隔离(Namespace):实现网络命名空间、PID命名空间、IPC命名空间、文件系统挂载、主机名和域名的隔离;
  • 资源限制(CGroups):限制资源包括 CPU、内存、块设备、Network等;
  • 分层存储(Union FS):实现容器镜像的物理存储与新建容器存储。

1.1.3 容器的网络

1. Docker 网络模式
  • host 模式:如果启动容器时候,使用 host 模式,那么容器将不会获得一个独立的Network Namespace ,而是和宿主机共用一个 Network Namespace。一个 Newwork Namespace 提供了一份独立的网络环境,包括网卡、路由、iptables规则等。

    docker run -d --name my_container --network host nginx
    
  • bridge模式(网桥):Docker 默认会创建一个 docker0 网桥(其上有一个 docker0 接口) ;

    #创建一个新的桥接网络
    docker network create --driver bridge my_bridge
    
    #查看网络
    docker network ls
    
    #运行容器并指定网络
    docker run -d --name my_container --network my_bridge nginx
    
  • none模式:此模式下容器不参与网络通信,运行于此类容器中的进程仅能访问本地环回接口,仅适用于进程无须网络通信的场景中,例如备份,进程诊断及各种离线任务等;

    docker run -d --name my_container --network none nginx
    

    --network=none:设置模式容器工作在none模式下。

2. Docker 网络启动过程

Docker 服务启动时会首先在主机上创建一个 docker0 的虚拟网桥,这个网桥相当于一个虚拟的交换机,连接了宿主机和所有 Docker 容器的网络。 Docker 随机分配一个本地未占有的私有网段中的一个地址给 docker0 接口,默认是 172.17.0.01/16网段。 此后启动的容器会自动分配一个该网段的地址。 当创建一个 Docker 容器时,同时会创建一对 veth pair 互联接口。互联接口的一端位于容器内,即 eth0 ,另一端在宿主机并被挂载到 docker0 网桥,名称为 veth 开头。当向任一接口发送消息时,另外一个接口自动收到相同的包。通过这种方式,主机可以与容器通信,容器之间也可以相互通信。如此一来,Docker 就创建了在主机和所有容器之间的一个虚拟共享网络。 通过上面的介绍,容器默认情况下可以访问到宿主机本地网络,容器间也可以互相访问。 接下来处理外部和容器的通信问题,如果容器想要通过宿主机访问外部网络,则需要宿主机进行辅助转发。

3. Docker 网络连通性

容器访问外部

容器默认指定了网关为docker0网桥上的docker0内部接口。docker0内部接口同时也是宿主机的一个本地接口。因此,容器默认情况下可以访问到宿主机本地网络。如果容器要想通过宿主机访问到外部网络,则需要宿主机进行辅助转发(SNAT)。

1.在宿主机Linux系统中,检查转发是否打开:

[root@docker ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

#如果为0,说明没有开启转发,则需要手动打开
sysctl  -w net.ipv4.ip_forward=1

2.假设容器内部的网络地址为172.17.0.2,本地网络地址为10.0.2.2。容器要能访问外部网络,源地址不能为172.17.0.2,需要进行源地址映射(Source NAT,SNAT),修改为本地系统的IP地址10.0.2.2。映射是通过iptables的源地址伪装操作实现的。

外部访问容器

容器允许外部访问,可以在docker [container] run 时候通过 -p 或 -P 参数来启用。底层是目标地址映射(DNAT)。

不管用哪种办法,其实也是在本地的 iptable 的 nat 表中添加相应的规则,将访问外部IP地址的包进行目标地址DNAT,将目标地址修改为容器的 IP 地址。

容器间互相访问

容器默认都连接在 Docker0 网桥上,都可以互相访问,相当连于在一台二层交换机上。如果对隔离性有要求,也可以创建多个自定义网络(即网桥),同一个自定义网络下的可以实现互联互通。

#创建一个自定义网络
docker network create wp-net

#查看自定义网络
docker network  ls 

#运行容器
docker run -d --name db_wordpress --restart always --network wp-net 
docker run -d --name wordpress --restart always --network wp-net -e WORDPRESS_DB_HOST=db_wordpress:3306

1.2 容器的管理

#查看Docker详细信息
docker info 

#镜像搜索
docker search nginx

#拉取镜像 
dokcer pull

#推送镜像到仓库
docker tag nginx:latest xxx.xxx/nginx:v1
docker push xxx.xxx/nginx:v 1 

#启动时进入容器 
docker run -it nginx bash

#容器放在后台运行 
docker run -d nginx 

#容器开启外部访问 
docker run -d -p 111:80 nginx 
#本机111端口映射到容器的80端口 

#容器挂载本地目录或文件 
docker run -d -v /etc/hosts:/etc/hosts 
#前面是本机,后面是容器

#创建一个自定义网络,并将容器置于此网络下 
docker network create my-bridge-test 
docker run --network my-bridge-test 

#资源限制
docker run --cpus=2 docker run -m 200M

#查看正在运行的容器 
dokcker ps

#查看所有容器 
docker ps -a 

#查看所有容器并且只显示容器id 
docker ps -aq 

#进入一个容器中 
docker exec -it 容器名 bash 

#将文件复制到容器中 
docker cp /root/test 容器名:/tmp 

#删除容器 
docker rm 容器名 
docker ps -a |grep Exited 

#删除镜像 
docker rmi 镜像名 

#制作镜像 
docker build -t xxx:TAG . 

#查看日志 
docker logs -f 容器名 

#查看容器信息 
docker inspect 容器名 

#查看镜像信息 
docker history 

#列出悬空镜像的id 
docker images -f “dangling=true" -q

1.3 Docker 镜像制作

1.3.1 Dockerfile 文件

什么是 Dockerfile 文件

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

FROM			#引用基础镜像(本地无则去拉取) 
USER			#切换用户 
ENV				#设置环境变量 
ARG				#设置环境变量,只作用于镜像制作过程中 
COPY			#将本地文件复制到待制作的镜像中 
ADD				#将本地文件或者URL的文件复制到待制作的镜像中,并实现自动解压缩 
RUN				#在新镜像中需要运行的shell 
EXPOSE			#需要导出端口约定,只是约定,并不是实际映射端口,需要run命令实际制定内外端口映射
CMD				#容器启动默认执行命令,分为三种模式,shell模式、ENTRYPOINT参数模式、exec模式 
# 可以通过docke run重新指定而覆盖默认命令 
ENTRYPOINT		#容器启动默认执行命令分为两种模式,shell模式、exec模式
# 不可以通过docker run覆盖,只能传递参数 WORKDIR 切换工作目录 
案例:使用 Dockerfile 定制镜像
#FROM 后面跟基础镜像地址,此示例可以理解为安装了bes软件的系统,下面的运行命令基于此系统运行
FROM 基础镜像 

#切换为root用户,后面的命令以root用户来执行
USER root 

#设置环境变量
ENV LANG zh_CN.UTF-8 ENV LC_CTYPE zh_CN.UTF-8 ENV USNAME lxf ENV USID 601

#RUN后面为linux 命令,groupadd和useradd相当于在基础镜像系统内创建属组和用户
RUN groupadd ${USNAME} -g ${USID} ;useradd ${USNAME} -u ${USID} -g ${USNAME} -G bes -d /${USNAME} 

#COPY 将文件拷贝到容器内的某个目录下,--chown 同时给文件设置用户和属组
COPY --chown=swd:swd ./target/*.war /${USNAME}/app/

#代码编译后生成的文件
COPY --chown=swd:swd ./boot/bootstrap.sh /${USNAME}/

#上传到码云上的启动脚本 
COPY --chown=swd:swd ./lib/* /${USNAME}/lib/

#RUN 后面chmod 设置镜像内某个文件的权限
RUN chmod -R 755 /${USNAME}/bootstrap.sh RUN chmod -R 775 /bes 

#WORKDIR,设置工作目录,即基于此镜像创建的容器启动后的默认目录
WORKDIR /${USNAME}

#USER 切换用户,后面的命令以该用户来执行
USER ${USNAME}

#EXPOSE 声明容器运行时提供服务的端口,只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务
EXPOSE 8080 80 443

#ENTRYPOINT 容器启动时,会根据这里设置的启动脚本来自动启动容器内的应用服务,作为容器主进程存在,如果该启动脚本正常运行且应用服务正常起来,则容器起来后状态正常,否则容器将无法起来
ENTRYPOINT ["/bin/sh","-c","/swd/bootstrap.sh"] 

Dockerfile 文件注意事项:

  • Dockerfile 文件名称为 Dockerfile,不加任何后缀;
  • Dockerfile 文件格式为unix格式,请注意文件格式。

1.3.2 镜像大小优化

优化 Dockerfile 的几个关键点包括选择合适的基础镜像、合并指令、清理缓存以及使用多阶段构建。

示例
# 第一阶段:构建阶段
FROM maven:3.9.0-openjdk-17 AS builder

# 设置工作目录
WORKDIR /app

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

# 使用 Maven 构建项目
RUN mvn clean package -DskipTests

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

# 创建应用程序目录
WORKDIR /app

# 复制 JAR 文件
COPY --from=builder /app/target/myapp.jar ./myapp.jar

# 设置容器启动命令
CMD ["java", "-jar", "myapp.jar"]

优化要点解析

  1. 选择合适的基础镜像
    • 在构建阶段,使用 maven:3.9.0-openjdk-17,它包含了 Maven 和 OpenJDK 17,适合编译 Java 应用。
    • 在运行阶段,选择 openjdk:17-jdk-slim,这是一个精简版本的 JDK 镜像,避免了不必要的工具和库,从而减小镜像体积。
  2. 避免安装不必要的工具
    • 通过选择 openjdk:17-jdk-slim,确保只安装运行 Java 应用所需的最小依赖,减少了安全风险和资源占用。
  3. 多阶段构建
    • 使用多阶段构建可以显著降低最终镜像的大小,只包含运行时所需的 JAR 文件。

此外还可以通过合并 RUN 指令来减少层数。

0

评论区