# 一.基础概念

# 1.什么是 docker?

Docker 是一个开源的应用容器引擎,基于 go 语言开发并遵循了 apache2.0 协议开源。 Docker 是在 Linux 容器里运行应用的开源工具,是一种轻量级的虚拟机。 Docker 的容器技术可以在一台主机上轻松为任何应用创建一个轻量级的,可移植的,自给自足的容器。 也可以这样形象的比喻:Docker 的 Logo 设计为蓝色鲸鱼,拖着许多集装箱,鲸鱼可以看作为宿主机,集装箱可以理解为相互隔离的容器,每个集装箱中都包含自己的应用程序。

# 2.docker 有哪些优点

容器化越来越受欢迎,Docker 的容器有点总结如下:

  • 灵活:即使是最复杂的应用也可以集装箱化。
  • 轻量级:容器利用并共享主机内核。
  • 可互换:可以即时部署更新和升级。
  • 便携式:可以在本地构建,部署到云,并在任何地方运行。
  • 可扩展:可以增加并白动分发容器副本。
  • 可堆叠:可以垂直和即时堆叠服务。

# 3.docker 架构

Docker 的架构涉及多个组件,它们协同工作以实现容器化应用程序的构建、分发和运行。以下是 Docker 的基本架构组件:

  1. Client (Docker 客户端):Docker 客户端是用户与 Docker 交互的主要接口。用户可以使用命令行工具(如 docker 命令)或图形用户界面来与 Docker 进行交互。客户端将用户的命令和请求发送到 Docker Daemon 来执行。

  2. Docker Daemon (Docker 守护进程):Docker Daemon 是 Docker 引擎的后台服务,负责管理和运行容器。它监视来自 Docker 客户端的命令,处理容器的生命周期,管理镜像、网络、存储等各种操作。

  3. Docker Images (Docker 镜像):Docker 镜像是一个只读的文件系统快照,其中包含了应用程序运行所需的文件、库和依赖项。镜像是容器的基础,可以用来创建容器的实例。镜像可以通过 Docker Hub 或其他镜像仓库获取,也可以通过 Dockerfile 定义自定义镜像。

  4. Docker Containers (Docker 容器):容器是基于 Docker 镜像创建的运行实例。容器提供了一个隔离的环境,包括文件系统、进程空间和网络。每个容器都是独立的,可以在同一主机上并行运行多个容器。

  5. Docker Registry (Docker 仓库):Docker 仓库用于存储和分享 Docker 镜像。Docker Hub 是一个公共的 Docker 仓库,开发者可以在其中找到各种现成的镜像。除了公共仓库外,还可以搭建私有的 Docker 仓库以存储自定义镜像。

  6. Docker Compose:Docker Compose 是一个工具,允许用户使用一个 YAML 文件定义多个服务、网络和卷,从而可以通过单个命令启动、停止和管理多个容器。

  7. Docker Swarm:Docker Swarm 是 Docker 的集群和编排解决方案,允许用户在多台主机上创建和管理容器集群。它可以协调容器的部署、扩展和管理。

  8. Docker Machine:Docker Machine 是一个工具,用于在多种平台上创建、管理和维护 Docker 主机。它可以在虚拟机、云服务提供商等不同的环境中自动化地创建 Docker 主机。

这些组件一起构成了 Docker 的架构,使得开发者能够更轻松地构建、部署和管理容器化的应用程序。

image-20230308150444038

# 4.容器数据卷?

数据卷是一个供容器使用的特殊目录,位于容器中。可将宿主机的目录挂载到数据卷上,对数据卷的修改操作立刻可见,并且更新数据不会影响镜像,从而实现数据在宿主机与容器之间的迁移。数据卷的使用类似于 Linux 下对目录进行的 mount 操作。

如果需要在容器之间共享一些数据,最简单的方法就是使用数据卷容器。数据卷容器是一个普通的容器,专门提供数据卷给其他容器挂载使用。

容器互联是通过容器的名称在容器间建立一条专门的网络通信隧道。简单点说,就是会在源容器和接收容器之间建立一条隧道,接收容器可以看到源容器指定的信息

# 5.docker 和虚拟机区别?

  • vm 虚拟机是 VM OS 系统+虚拟机监视器
  • docker 是 docker engine docker 引擎
  • docker 是运行在宿主机内核上的,没有自己的内核,小巧
  • vm 是操作系统的虚拟化,docker 是内核级别的虚拟化

# 二.Dockerfile

# 1.什么是 Dockerfile

Dockerfile 是一个创建镜像所有命令的文本文件, 包含了一条条指令和说明, 每条指令构建一层, 通过 docker build 命令,根据 Dockerfile 的内容构建镜像,因此每一条指令的内容, 就是描述该层如何构建.有了 Dockefile, 就可以制定自己的 docker 镜像规则,只需要在 Dockerfile 上添加或者修改指令, 就可生成 docker 镜像。

Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。

# 2.文件格式

# 1、第一行必须指定 基础镜像信息
FROM ubuntu

# 2、维护者信息
MAINTAINER docker_user docker_user@email.com

# 3、镜像操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# 4、容器启动执行指令
CMD /usr/sbin/nginx
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3.构建镜像

docker build 命令用于从 Dockerfile 构建映像。可以在 docker build 命令中使用-f 标志指向文件系统中任何位置的 Dockerfile。-t 用于指定新构建的镜像的名称和标签。

docker build -t command-$PROFILE .
1

创建文件DockerFile

# 构建镜像
docker build -f /home/muse/dockerfiles/dockerfile01 -t muse/centos:1.0 .
docker build -f /path/to/a/Dockerfile .

# 启动镜像
docker run -it eb78333356a6 /bin/bash

# 推送镜像到远程仓库
docker pull  centos:1.0  qyj1992/centos:1.0
1
2
3
4
5
6
7
8
9

# 4.文件格式详解

Docker 以从上到下的顺序运行 Dockerfile 的指令

  • FROM:指定基础镜像,必须为第一个命令

  • MAINTAINER: 维护者信息

  • RUN:构建镜像时执行的命令

  • ADD:将本地文件添加到容器中,tar 类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似 wget

  • COPY:功能类似 ADD,但是是不会自动解压文件,也不能访问网络资源

  • CMD:构建容器后调用,也就是在容器启动时才进行调用。

  • ENTRYPOINT:配置容器,使其可执行化。配合 CMD 可省去"application",只使用参数。

  • LABEL:用于为镜像添加元数据

  • ENV:设置环境变量

  • EXPOSE:指定于外界交互的端口

  • VOLUME:用于指定持久化目录

  • WORKDIR:工作目录,类似于 cd 命令

  • USER:指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。使用 USER 指定用户时,可以使用用户名、UID 或 GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户

  • ARG:用于指定传递给构建运行时的变量

  • ONBUILD:用于设置镜像触发器

image-20230819231846614

# 5.CMD 和 ENTRYPOINT

RUNCMDENTRYPOINT是在 Dockerfile 中用于定义容器镜像构建和运行时行为的指令。它们各自有不同的作用,下面是它们的区别和联系:

  1. RUN

    • RUN指令在镜像构建过程中执行命令,并在每个构建层中创建新的镜像。它用于在容器镜像中安装软件、设置环境变量、下载文件等。
    • 任何在RUN指令中执行的命令都会在构建时运行,以生成新的中间镜像层。这些命令对于构建容器的静态配置和设置非常有用。
    • 示例:RUN apt-get update && apt-get install -y nginx
  2. CMD

    • CMD指令定义默认情况下容器运行时要执行的命令。它可以在 Dockerfile 中只有一个,指定容器启动时默认执行的命令。
    • 如果在docker run命令中指定了要运行的命令,它将覆盖CMD中定义的默认命令。
    • CMD通常用于定义容器启动时的默认行为,例如应用程序的启动命令。
    • 示例:CMD ["nginx", "-g", "daemon off;"]
  3. ENTRYPOINT

    • ENTRYPOINT指令也用于定义容器启动时要执行的命令,但它具有更高的优先级,不会被docker run命令中指定的命令覆盖。
    • ENTRYPOINT的参数会被视为容器的命令行参数,与 CMD 指令结合使用,可以创建可配置的容器镜像,其中ENTRYPOINT定义了应用程序的主要执行逻辑,而CMD则提供了默认参数。
    • 示例:ENTRYPOINT ["java", "-jar", "myapp.jar"],然后可以在docker run命令中提供额外的参数,例如docker run my-image arg1 arg2

联系与区别:

  • RUN用于构建时,在镜像构建过程中运行命令来创建新的镜像层。
  • CMD用于指定默认的容器启动命令,可以在 Dockerfile 中只有一个,也可以在运行容器时被替换。
  • ENTRYPOINT也用于定义容器启动时的命令,但它的参数不会被覆盖,与CMD结合使用可以创建更具灵活性的容器镜像。

通常情况下,CMDENTRYPOINT可以用来定义容器内的应用程序启动命令,而RUN用于构建容器镜像时的一次性操作。

# 6.时间不一致

在 Docker 容器中获取当前日期和时间的行为通常是与宿主操作系统的时区和时间设置有关的。如果 Docker 容器的时区设置不正确,可能会导致new Date()获取到的日期和时间不符合预期。

要解决这个问题,你可以采取以下步骤:

  1. 确保 Docker 容器的时区正确:在 Docker 容器中,时区通常默认设置为 UTC。你可以通过设置环境变量来更改容器的时区,以便其与你所期望的时区匹配。例如,在 Dockerfile 中,你可以添加以下行来设置时区:
ENV TZ=Asia/Shanghai
1

这将设置容器的时区为亚洲/上海。你需要根据你的实际时区进行设置。

  1. 使用java.time:建议使用java.time库来处理日期和时间,而不是使用java.util.Datejava.time库更加现代化,支持更精确的日期和时间操作,并且更容易受到时区设置的影响。例如,你可以使用LocalDateTime来获取当前日期和时间,然后根据时区进行处理,以确保得到正确的结果。
import java.time.LocalDateTime;
import java.time.ZoneId;

public class Main {
    public static void main(String[] args) {
        ZoneId zoneId = ZoneId.of("Asia/Shanghai");
        LocalDateTime now = LocalDateTime.now(zoneId);
        System.out.println(now);
    }
}
1
2
3
4
5
6
7
8
9
10

通过明确设置时区,你可以确保获取到的日期和时间是与你的预期一致的,而不会受到 Docker 容器的默认时区设置的干扰。

确保 Docker 容器的时区正确设置以及使用java.time库来处理日期和时间应该能够解决在 0 点到 1 点之间获取日期时出现的问题。

# 7.操作系统架构概览

在讨论--platform参数之前,我们需要了解一些常见的操作系统架构:

  • amd64:即 x86_64,是最常见的桌面和服务器 CPU 架构,广泛用于 Intel 和 AMD 的处理器。
  • arm64:ARM 架构的 64 位版本,常用于移动设备和一些高性能计算场景。
  • windows/amd64:指运行在 64 位 Windows 操作系统上的架构。

# 8.使用--platform构建 Linux amd64 镜像

假设我们需要为 Linux amd64 架构构建一个 Docker 镜像,可以使用以下命令:

docker build --platform=linux/amd64 -t my-image:latest .
1

这条命令指示 Docker 守护进程在构建过程中,生成一个适用于 Linux amd64 架构的镜像

参数 --platform=linux/amd64 指定了构建过程应该生成一个在 64 位 Linux 操作系统上运行的镜像。下面是该参数的详细解释:

  1. --platform:这是 Docker CLI 的一个全局选项,用于指定构建、拉取或推送操作的目标平台。

  2. linux:指定了操作系统平台。在这个例子中,它是linux,意味着目标平台是运行 Linux 操作系统的。

  3. amd64:指定了架构。amd64是 64 位 x86 架构的常见称呼,也被称作 x86_64。这表示镜像是为 64 位的处理器设计的。

当你运行带有 --platform=linux/amd64 参数的 docker build 命令时,Docker 会尝试构建一个仅适用于 64 位 Linux 系统的镜像。这很重要,因为不同的硬件架构可能需要不同的镜像,例如,你可能需要为 ARM 架构的系统构建一个不同的镜像。

# 9.缓存机制

缓存机制的工作原理是这样的:

  • 当构建 Docker 镜像时,Docker 会按照 Dockerfile 中的指令顺序执行。
  • 每条RUN指令都会创建一个新的镜像层,并且这个层会被缓存。
  • 如果 Dockerfile 中的指令没有变化,Docker 会使用缓存的层而不是重新执行命令。
  • 缓存的层会根据 Dockerfile 中的指令和上下文(如文件内容)生成一个唯一的哈希值。如果哈希值匹配,Docker 就知道可以重用缓存的层。

在实际应用中,这意味着如果requirements.txt文件没有变化,那么RUN pip install -r requirements.txt这条指令的结果就会被缓存,直到requirements.txt或之前的指令发生变化。

# 三.CentOS 安装 docker

# 1.移除以前的安装

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
1
2
3
4
5
6
7
8

# 2.配置 yum 源

sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
1
2
3
4

# 3.安装 docker

sudo yum install -y docker-ce docker-ce-cli containerd.io

#以下是在安装k8s的时候使用
yum install -y docker-ce-20.10.7 docker-ce-cli-20.10.7  containerd.io-1.4.6
1
2
3
4

# 4.启动

# 开机启动
systemctl enable docker --now
1
2

# 5.配置加速

# 创建目录
sudo mkdir -p /etc/docker

# 配置加速文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://82m9ar63.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

#重新加载文件
sudo systemctl daemon-reload

#重启docker
sudo systemctl restart docker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 6.验证安装

是否成功(有 client 和 service 两部分表示 docker 安装启动都成功了)

#查看版本
docker version
deepexi-dsc-belle-insight-kwan

# 启动容器,以镜像 soar/centos:7.1 创建名为 test 的容器,并以后台模式运行,并做端口映射到宿主机 2222 端口,P 参数重启容器宿主机端口会发生改变
docker run -d -p 2222:22 --name test soar/centos:7.1
1
2
3
4
5
6

# 四.docker 安装 nginx

# 1.下载 nginx 镜像

docker pull nginx
1

# 2.启动 nginx 容器

docker run --name nginx -p 3000:3000 -p 3002:3002 -p 8080:8080 -v /root/java/docker/nginx.conf:/etc/nginx/nginx.conf -v /root/java/web:/home -d nginx:stable
1

# 3.nginx 配置

user root;
worker_processes  1;

events {
worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;


    sendfile        on;
    keepalive_timeout  65;

    #server {
    #    listen       8080;
    #    #server_name  localhost;

    #    location / {
    #        proxy_pass http://10.201.1.98:3002;
    #  }

    #    error_page   500 502 503 504  /50x.html;
    #    location = /50x.html {
    #        root   html;
    #    }
    #}


    # abi
    server {
        listen       3000;
        #server_name  localhost;

        location / {
            root   /home/abi/build;
            index  index.html index.htm;
        }

        location /abi/ {
    	     proxy_pass http://10.201.1.84:8100/;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
        root   html;
        }
    }

    # fast-bi-test
    server {
        listen       3002;
        #server_name  localhost;

        location / {
            root   /home/fast-bi/test/build;
            index  index.html index.htm;
        }

        location /api/ {
            proxy_pass http://127.0.0.1:8899/;
        }

        error_page   500 502 503 504  /50x.html;
            location = /50x.html {
            root   html;
        }
    }

    # fast-bi-poc
    server {
        listen       8080;
        #server_name  localhost;

        location / {
            root   /home/fast-bi/poc/build;
            index  index.html index.htm;
        }

        location /api/ {
            proxy_pass http://127.0.0.1:8897/;
        }

        error_page   500 502 503 504  /50x.html;
            location = /50x.html {
            root   html;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

# 4.nginx 目录

/etc/nginx
/usr/local/nginx/conf/nginx.conf
/usr/local/nginx/sbin/
1
2
3

# 5.nginx 命令

#指定配置文件启动
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

#查看是否启动
ps -ef|grep nginx

#停止
kill -9 nginx

#在目录sbin下,重启nginx
./nginx -s reload
1
2
3
4
5
6
7
8
9
10
11

# 五.部署文档

# 1.镜像包

# 注意rv-server和abi-web 服务名
docker pull deploy.xxx.com/kwan-dev/rv-server:20220506220402
docker tag deploy.xxx.com/kwan-dev/rv-server:20220506220402 rv-server:0407-01
1
2
3

# 2.执行脚本

docker stop 容器id
docker rm 容器id

docker rmi 镜像名称:tag
docker rmi 镜像id

#改tag名称:
docker pull 镜像地址
docker tag 镜像名称:tag 新的镜像名称:tag

docker run -d --net=host -v /home/logs:/home/logs --privileged=true \
-e server.port=8200 \
-e spring.datasource.dynamic.datasource.mysql.url="jdbc:mysql://xxxxxxxx:3306/rv?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai" \
-e spring.datasource.dynamic.datasource.mysql.username=root \
-e spring.datasource.dynamic.datasource.mysql.password=xxxx \
-e spring.datasource.dynamic.datasource.click.url="jdbc:clickhouse://xxxxx:8123/default?socket_timeout=300000" \
-e spring.datasource.dynamic.datasource.click.username=root \
-e spring.datasource.dynamic.datasource.click.password=xxxx \
-e nlp.http.post.url=http://xxxx:8300/ \
-e security.oauth2.signing-key=deepexi-@Jwt!&abi-demo^# \
-e security.oauth2.cache=true \
-e security.oauth2.token-expired-time=60 \
-e spring.redis.host=10.201.2.24 \
-e spring.redis.port=6379 \
-e spring.redis.password=xxxxx \
rv-server:1.1.3-hotfix
#最后改镜像名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 3.Dockerfile 配置

FROM devops-harbor.crv.com.cn/library/jdk8u202-skywalking7:v1

ARG BASE_DIR=/home
ENV BASE_DIR ${BASE_DIR}
WORKDIR ${BASE_DIR}
RUN mkdir -p ${BASE_DIR}
ADD startup.sh ${BASE_DIR}/startup.sh
RUN chmod +x ${BASE_DIR}/startup.sh
ADD target/*.jar ${BASE_DIR}/app.jar
ENTRYPOINT ["/home/startup.sh"]
1
2
3
4
5
6
7
8
9
10

# 六.线上调优

# 1.docker stats

docker stats 是一个用于查看 Docker 容器资源使用情况的命令。它提供了实时的统计数据,包括 CPU 使用率、内存使用量、网络 I/O 和块 I/O 等信息。

以下是 docker stats 命令的基本语法:

docker stats [OPTIONS] [CONTAINER...]
1

主要选项包括:

  • --all-a:显示所有容器的统计信息,包括停止状态的容器。
  • --no-stream:只显示当前的统计信息,然后退出而不持续更新。

你可以通过指定容器名称或 ID 来限制 docker stats 命令的输出,例如:

docker stats container1 container2
1

在命令的输出中,你将看到以下信息:

  • CONTAINER:容器的名称或 ID。
  • CPU %:CPU 使用率。
  • MEM USAGE / LIMIT:内存使用量和限制。
  • MEM %:内存使用率。
  • NET I/O:网络 I/O 数据量。
  • BLOCK I/O:块 I/O 数据量。
  • PIDS:进程 ID 数量。

docker stats 命令提供了一个实时监控容器资源使用情况的方式,可以帮助你了解容器的性能和资源消耗情况。你可以使用 Ctrl+C 终止命令的运行。

希望这个解释对你有帮助!如有其他问题,请随时提问。

# 2.占用内存高,如何排查?

如果使用 docker stats 命令发现某个容器的内存占用较高,可以采取以下步骤来进一步调查和解决问题:

  1. 确定具体容器:docker stats 命令会列出所有正在运行的容器的统计信息。找到内存占用较高的容器,并记住容器的名称或 ID。

  2. 进一步了解容器:运行 docker inspect <容器名称或 ID> 命令,以获取有关容器的详细信息。在输出中,你可以查看容器的配置、资源限制和挂载点等信息,这可能有助于确定问题的根本原因。

  3. 查看容器日志:使用 docker logs <容器名称或 ID> 命令来查看容器的日志,以寻找任何与内存使用相关的错误或异常信息。

  4. 分析容器进程:通过 docker top <容器名称或 ID> 命令查看容器内部正在运行的进程。这将显示容器内部进程的列表以及它们的资源使用情况,包括内存。检查是否有某个进程异常占用内存。

  5. 进入容器进行诊断:使用 docker exec -it <容器名称或 ID> /bin/bash 命令进入容器的终端,进一步分析容器内部的情况。在容器内部,你可以运行各种命令来检查运行的应用程序、运行中的服务或其他进程。

  6. 优化容器配置:如果发现容器的内存占用确实过高,可能需要调整容器的资源限制。可以通过 docker update --memory=<内存限制> <容器名称或 ID> 命令来修改容器的内存限制。确保为容器分配合适的资源,并根据应用程序的需求进行调整。

  7. 检查应用程序:高内存占用可能是应用程序本身的问题。检查容器内运行的应用程序的内存使用情况,确保应用程序没有内存泄漏或其他资源管理问题。

这些步骤将帮助你更好地了解容器的内存使用情况,并找出可能的问题和解决方案。记得逐步排查,根据具体情况采取相应的措施。

上次更新: 12/1/2024, 5:52:32 PM