Docker使用入门

Docker

如今的 IT 企业已逐渐从以 ESB 为中心的面向服务架构过渡到微服务架构。前端工程师需要学会将项目构建为镜像提交,后端工程师需要能够容器化构建和部署软件,运维人员需要设计新的微服务运维架构,小型企业需要依靠云原生平台减少生产成本,中大型企业需要将内部私有云平台升级以支持微服务。。。Docker 作为目前各大公有和私有云平台中最常使用的容器技术,值得所有 IT 技术领域从业人员深入学习。

简而言之,Docker 的出现是为了解决开发、测试和生产环境间存在的差异性,以减少过程成本,使得项目能够快速部署上线,而不需要花过多时间在环境配置和迁移上。即,通过软件带环境安装减少各平台间差异性的影响。

概念

Docker 的理念

“一次构建,处处运行。” Docker 是解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体发布的容器虚拟机。

容器和虚拟机

虚拟机是带环境安装的一种解决方案,传统的虚拟机在操作系统中运行另外一些操作系统。传统虚拟机的主要问题在于:

  • 资源占用大
  • 冗余步骤多
  • 启动速度慢

传统虚拟机模拟了所有软件层面的资源,这是没有必要的。实际上,依赖于操作系统的软件需要的只是操作系统的一些内核服务。

Linux 容器(Linux Container,LXC)是 Linux 上的另一种虚拟化服务,该容器本质上是一系列和其他进程与资源所隔离的进程,而不是一个完整的操作系统,只拥有软件需要的资源,即是最小化的操作系统。

Docker 容器是内核级虚拟化技术,只负责虚拟化软件所必须依赖的系统内核服务。Docker 作为容器技术的一种,与虚拟机支撑的操作系统有如下区别和优势:

  • 无需虚拟硬件
  • 容器内应用实际上直接运行于操作系统内核,容器不需要自己的内核
  • 容器间相互隔离,即有自己的文件系统,不会互相影响
  • 镜像复用

简而言之,容器更轻量,更迅速。

新技术与新架构

Docker 容器技术实际上促成了新的岗位:开发/运维工程师(DevOps Engineer)的出现。由于容器技术降低了整个过程的成本, 软件工程师可以直接参与容器层的简单运维,而不需要特别的运维人员参与到应用部署过程。在微服务和容器技术的场景下,部署新应用就像搭积木一样,而扩容应用也变得更加简单。

Docker 基本组成

Docker 三要素:

  • 镜像(image):可复用的环境刻录,至少包含一个 root 文件(最小的 Linux 单元)用于运行
  • 容器(container):刻录的实例化
  • 仓库(repository):存放刻录的存储器

Docker 架构

Docker 只支持 Linux 操作系统。如果要在其他系统使用,需要建立 Linux 系统的虚拟机。

Docker 采用 CS 架构,由一个守护进程(Daemon,也可以叫做服务进程)作为服务器管理所有容器,接收来自 Docker 本地或远程客户端(命令行或 GUI)的命令。CS 间的通信服务由套接字提供。Docker 基本运行流程:

  1. 用户使用 Docker Client 与 Docker Daemon(Service)通信,向后者发送请求;
  2. 守护进程是 Docker 架构的主体,首先提供 Docker Server 接收 Docker Client 请求;
  3. Docker Engine 负责执行 Docker 内部的工作(Job);
    • Job 运行时,如果需要使用镜像,则从 Docker Registry 中下载镜像,并通过镜像管理驱动(Graph Driver)以 Graph 形式存储;
    • 如果容器需要网络环境,由网络管理驱动(Network Driver)负责创建容器的网络环境;
    • 如果需要执行用户指令,或限制容器资源,由执行驱动(Exec Driver)负责;
  4. 此外,还有一个独立的容器管理库 libcontianer,各个驱动都通过该库提供的过程实现对容器的操作。

Docker使用入门_第1张图片

镜像层级

Docker 基于联合文件系统(UnionFS)制作镜像。联合文件系统是一种分层、轻量级的文件系统,支持对文件的修改作为一次提交来层层叠加,并可以将不同目录挂载到同一个虚拟文件系统下。通过分层,Docker 可以在一些基础镜像下制作出更加具体的应用镜像。当镜像启动时,通过层层加载来加载整个容器。

Docker 镜像的最底层就是引导文件系统(bootfs),由 boot loader 和内核(kernel)组成。在 Linux 系统中,当 boot loader 将整个 kernel 加载至内存后,内存使用权将移交给内核,bootfs 随即被卸载,根文件系统 rootfs 则建立在 bootfs 之上。Docker 系统镜像只需要 bootfs 即可运行,由镜像的 bootfs 复用宿主机内核。

当镜像被加载时,一个可写层即容器层会被加载到层级顶部,容器层之下被称为镜像层,所有镜像层都是只读的。

容器数据卷

容器数据卷用于完成容器数据的持久化,用于备份容器资料。Docker 可以将容器目录映射到宿主机目录,从而达到备份目的。

数据卷就是目录或文件,存在于一个或多个容器中,由 Docker 挂载到容器,但不属于 UFS。数据卷生命周期完全独立于容器,删除容器时不会删除容器卷。

  • 容器数据卷可在多个容器间共享数据;
  • 卷中数据的更改实时生效,不用像 cpexport 命令一样手动备份;
  • 数据卷更改不包含镜像更新中;
  • 数据卷的生命周期将持续到没有容器使用其为止。
$ docker inspect <容器 id> # 查看容器信息,Mounts 一栏包含了容器卷映射信息
"Mounts": [
            {
                "Type": "bind",
                "Source": "/root/apline-root",
                "Destination": "/root",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],

在映射地址对后添加:rw(默认)使容器可以读写宿主文件和目录内容,添加:ro限制为容器只读宿主文件和目录内容。

使用--volumes-from=<容器 id>继承另一个容器的卷映射规则。

安装

Linux CentOS

文档

安装需要的编译环境:

yum -y install gcc
yum -y install gcc-c++

安装 yum 工具并设置源:

yum install -y yum-utils
yum-config-manager --add-repo http://mirrors.aliyun.com/repo/Centos-7.repo # 可选,其他软件的阿里源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache fast # 可选,重建索引以加速

安装 Docker 引擎:

yum -y install docker-ce docker-ce-cli containerd.io
systemctl start docker

阿里免费镜像加速服务

测试:

docker version
docker run hello-world

Desktop Windows

Windows 版 Docker 有时会遇到System.InvalidOperationException错误,执行以下命令并重启 Docker:

netsh winsock reset

在 Windows 中,Docker 一般使用 wsl2 后端,所有镜像文件放置在 wsl2 目录中。如果需要改变镜像存放位置,必须改变 wsl 的安装目录。

  1. 退出Docker Desktop

  2. 关闭WSL

    wsl --shutdown
    
  3. 将子系统导出为tar文件

    wsl --export docker-desktop E://docker-desktop//docker-desktop.tar
    wsl --export docker-desktop-data E://docker-desktop//docker-desktop-data.tar
    
  4. 注销子系统

    wsl --unregister docker-desktop
    wsl --unregister docker-desktop-data
    
  5. 使用新路径导入子系统(注意,导出目录不能进行磁盘压缩)

    wsl --import docker-desktop E://docker-desktop//distro E://docker-desktop//docker-desktop.tar --version 2
    wsl --import docker-desktop-data E://docker-desktop//data E://docker-desktop//docker-desktop-data.tar --version 2 
    
  6. 启动Docker Desktop

  7. 删除不再需要的tar

命令

启动和帮助

systemctl start docker
systemctl stop docker
systemctl restart docker
systemctl status docker
systemctl enable docker # 开机启动
docker info
docker [cmd] --help # 查看帮助

镜像

$ docker images # 查看本地镜像 -a 展示所有 -q 只显示 id
REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
hello-world   latest    feb5d9fea6a5   5 months ago   13.3kB

$ docker search # 搜索镜像
$ docker pull # 拉取镜像
$ docker system df # 查看镜像/容器/卷占用的空间
$ docker rmi # 根据镜像的 id 或名字删除镜像 -f 强制删除有容器实例的镜像
$ docker rmi $(docker images -qa) # 删除所有镜像

$ docker images ls -f dangling=true # 查看所有虚悬镜像
$ docker images prune # 删除所有虚悬镜像

同一个镜像可以有多个带 TAG 的版本,默认情况下在命令中的镜像名会自动转换成 REPOSITORY:latest。如果要在拉取或打包时指定版本,在REPOSITORY后加上:version。有时 Docker 构建错误会产生一些虚悬镜像(dangling image,标签和库名都为),可以通过 id 删除。

容器

运行容器
docker run [opt] <image> [cmd] [args]

参数:

  • --name=<容器名>:为启动的容器命名;
  • --privileged=true:开启特殊权限,使容器能够访问一些宿主文件系统;
  • -d:后台运行容器,并返回容器 id,即启动守护式(后台)容器;
  • -i:启动交互式容器,一般配合-t使用;
  • -t:为容器分配一个伪终端;
  • -P:随机端口映射;
  • -p host:container:指定端口映射,外部访问端口 host 会映射到容器内的端口 container,可以有多次输入代表多个映射;
  • -v host:container:开启一个带容器卷的容器,将容器内路径 container 映射到宿主机的 host 路径,可以有多次输入代表多个映射;
    • -v host:container:rw:容器可读写主机文件;
    • -v host:container:ro:容器只读主机文件;
    • --volumes-from=<容器 id>:继承另一个容器的卷映射规则;

以 Ubuntu 为例,在容器内运行该系统,并指定 Shell:

$ docker run -it ubuntu bash # 要退出,使用 exit
显示正在运行的容器
$ docker ps
CONTAINER ID   IMAGE           COMMAND       CREATED         STATUS         PORTS     NAMES
98e3442472ec   centos:latest   "/bin/bash"   5 seconds ago   Up 4 seconds             beautiful_morse

参数:

  • -a:显示包括历史运行过的容器;
  • -l:显示最近创建的容器;
  • -n :显示最近创建的 n 个容器;
  • -q:只显示容器编号;
在交互式界面退出容器
$ exit # 退出当前容器前台终端,会导致容器停止
# 快捷键 ctrl+p+q 仅断开对容器前台终端的操作权
进入正在运行的容器
$ docker exec -it <容器名> [系统命令] # 在正在运行的容器中打开一个新的终端并执行命令,因此 exit 退出不会关闭容器
$ docker attach <容器> # 重新进入容器已经打开的终端,exit 相当于关闭已有终端,在没有前台程序的情况下会关闭容器
操作已经停止的容器
$ docker start <容器名或id>
$ docker restart
$ docker pause
$ docker unpause
$ docker stop
$ docker kill # 强制停止
$ docker rm # 删除容器
$ docker rm -f $(docker ps -a -q) # 删除所有容器
$ docker ps -a -q | xargs docker rm -f # 利用管道符和返回值连接删除所有容器
守护模式运行
$ docker run -d <images>

在使用 Docker 的守护模式运行即后台运行容器时,容器必须有一个前台进程,否则 Docker 会在所有前台进程结束后停止容器,不管有无后台进程存在。

查看日志
$ docker logs
查看容器内信息
$ docker top <容器名> # 查看容器内进程信息
$ docker inspect <容器名> # 查看容器详细信息

每个容器其实都是一个简易 Linux,包含基本环境和用户进程。

将容器打包为镜像
$ docker commit -m="description..." -a="author" <容器 id> <镜像名>[:标签] # 提交镜像副本为一个新镜像
拷贝容器内文件至外部宿主操作系统
$ docker cp <容器id:容器内路径> <宿主路径>
备份容器至外部与恢复
$ docker export <容器> > <路径/文件名.tar.gz> # 导出归档并压缩,注意箭头符
$ docker import <备份文件> <[用户名/]镜像名[:标签]> # 恢复备份
$ cat <备份文件> | docker import - <[用户名/]镜像名[:标签]>

注意:在 PowerShell 中,使用上述命令导出的 tar 包可能会出现文件头损坏问题,应该带上附加参数-o

docker export  -o .tar.gz>

私有仓库

Docker Registry 是用于搭建私有镜像仓库的工具。

$ docker pull registry # 下载工具
$ docker run --name="docker-registry" -d -p 43001:5000 -v /root/docker-registry:/tmp/registry --privileged=true registry

# 检查服务运行状态,查看已有镜像
$ curl -XGET http://your_service_name/v2/_catalog

# docker 客户端推送时默认使用 http 协议,如果要开启不安全 http 支持,更改客户端配置文件
$ vi /etc/docker/daemon.json
{
  "registry-mirrors": ["https://xxx.mirror.aliyuncs.com"],
  "insecure-registries": ["127.0.0.1:43001"]
}

Docker File

Dockerfile 文件是用于构建镜像内容的文本文件,类似 Shell 脚本,包含构建指令和参数。文档

Dockerfile 介绍

Dockerfile 特点:

  1. 保留字(指令)必须大写;
  2. 自上而下执行;
  3. #用于注释;
  4. 每条指令都将创建一个新的镜像层并提交。

Docker 执行 Dockerfile 的大致流程:

  1. Docker 运行 Dockerfile 指定的基础镜像容器;
  2. 执行指令,并提交镜像层;
  3. 继续执行直到所有指令完成。

Dockerfile 指令

FROM:基础镜像来源;

MAINTAINER:镜像作者,一般包括名字和地址,格式一般为author

RUN:容器构建时执行的命令,支持 shell 脚本和 exec 格式(RUN [<命令>, [参数1], [参数2]]);

EXPOSE:容器向外暴露的端口,类似-p参数;

WORKDIR:登录进终端后默认路径;

USER:指定用户,默认为 root;

ENV:设置环境变量,如ENV MY_PATH /usr/mytest,该变量只能被之后的 RUN 识别;

VOLUME:容器卷挂载设置,host:container[:读写权限字符串]。在使用docker run时,-v后的容器映射目录只能是 VOLUME 所暴露的目录,否则无效。如果没有显示指定-v参数,容器卷会映射到宿主机上一个由 docker 托管的目录下,默认/var/lib/docker/volumes/,该路径只能由宿主机 root 用户访问,因此会更加安全。此外 VOLUME 只能指定目录,不能指定文件;

ADD:将主机目录中文件拷贝至镜像指定目录,host:container,会自动处理归档压缩的tar.gz文件以及 URL;

COPY:将主机目录中文件拷贝至镜像指定目录,host:container

CMD:容器执行后要执行的命令,可以有多个,但只有最后一个会生效,并且会被docker run后的运行命令覆盖;

ENTRYPOINT:容器运行时执行的命令,一般会是一个 shell 脚本文件,不会被docker run运行命令覆盖,且docker run的运行命令以及CMD的命令会被作为参数传入该脚本,如:

ENTRYPOINT nginx -c
CMD /etc/nginx/nginx.conf

相当于:

$ nginx -c /etc/nginx/nginx.conf

如果容器运行时指定了运行参数,会覆盖 CMD 中的参数:

$ docker run <image> /etc/nginx/new.conf
# 实际运行的命令如下
$ nginx -c /etc/nginx/new.conf

构建命令

docker build -t <镜像名:标签> ,使用.代替路径会根据当前目录下的 Dockerfile 构建镜像。

  • -t:指定标签

Docker Network

概念

宿主机中的虚拟机连接网络的方式一般有以下几种:

  1. 桥接模式(bridge):虚拟机使用一个软件模拟的虚拟网络适配器(软网卡,如 VMWare 的 VMnet0 网卡)连接宿主机物理网络适配器(硬网卡),直接连接物理网络。此时虚拟机网络使用子网中的真实 IP 地址,对于网络系统来说就像一台真实主机一样;
  2. 网络地址转换模式(NAT):和子网 NAT 技术类似,让宿主机中的虚拟机软件(如 VMWare 的 VMnet8 网卡)进行 NAT ,对外部物理网络而言虚拟机只是一个套接字端口;
  3. 主机模式(Host):主机模式由宿主机提供一个封闭网络,虚拟机只能通过虚拟机软件(如 VMWare 的 VMnet1 网卡)和宿主机通信而不能直接访问互联网。

Docker Network 实现了桥接、主机、容器和自定义模式,常用桥接模式。Docker Network 将负责容器间的互联和端口映射。

Docker 四种网络模式:

模式 介绍
bridge 桥接,为每个容器分配局域网 IP,并将其连接到docker0虚拟网桥,使用docker run 命令运行容器时,容器默认使用桥接网络模式。
host 主机,容器没有自己的虚拟网卡和 IP,而是使用宿主机的 IP 和网卡。
none 无网络,容器有自己的网络命名空间,但是没有进行任何网络设置,没有 IP 和网桥,无法联网,只能使用本地环路。
container 容器,使用该模式的容器和另一个容器共享 IP 与端口。

桥接

在 Docker 守护进程已经启动的 Linux 主机中使用ifconfig命令查看网卡设备概览,可以发现一块名为docker0的虚拟网卡:

$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:6c:b2:e1:c3  txqueuelen 0  (Ethernet)
        RX packets 125553  bytes 44995127 (42.9 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 121826  bytes 498351039 (475.2 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        
# 内容解释:
# 第一行:分别是网卡名称、flag标志位(尖括号内为解析结果)、以太网最大传输单元
# flag各位含义:UP/DOWN网络启用或关闭,RUNNING/STOP网卡设备已连接/断开,MULTICAST支持组播(向特定方多播),BROADCAST支持广播(无差别多播)
# 第二行:inet本机ip,netmask子网掩码,broadcast广播地址
# 第三行:ether网卡mac地址,txqueuelen传输缓冲区大小,(Ethernet)表示局域网是以太网
# 第四行:Rx packets接收的分组总量,bytes大小统计
# 第五行:其他接收分组统计信息
# 第六行:TX packets发送的分组总量和大小
# 第七行:其他发送分组统计

# 其他常见网卡:eth0本机物理网卡,lo本地环路虚拟网卡

虚拟网卡docker0作为虚拟网桥,负责容器与宿主机、容器与容器间的通信。该网卡在内核层连通了其他物理或虚拟网卡,将容器放置在和宿主机相同的网络环境中。

docker0虚拟网桥类似容器的交换机,每个容器有自己的网卡(Linux 系统中一般名为eth0)端口,容器通过这些端口连接到交换机的入口端口veth上,交换机有多个veth端口。每一个eth0veth对被称为veth对(veth pair)。docker0 行使网关职能,使用自己的 IP 网段为所有容器分配 IP 地址。

使用ifconfig命令查看网卡时,veth开头的设备即为容器的虚拟网卡。

主机

主机模式下,容器使用和宿主机相同的网络命名空间,直接使用宿主机的 IP 与网卡设备。使用docker run --network host开启主机模式网络,由于主机模式下容器直接使用宿主端口,因此-p参数设置的端口映射是无效的,如果宿主机端口已经被占用则端口递增。

容器

容器模式下,使用另一个容器的网络 IP 配置。使用docker run --network container:<容器名>开启容器模式网络。该模式下不能使用端口映射功能,容器依赖于提供网络服务的容器,如果提供网络服务的容器关闭,则使用容器模式网络的容器也将失去网络连接。

自定义

由于在网桥模式中,各容器的 IP 地址由 Docker 网关负责分配,在进行容器迁移时必须同时关注容器新的 IP 地址。为了能够进行容器间的服务发现,需要使用服务名来访问容器实例。

使用自定义网络模式,可以很好地通过容器名进行容器间服务发现,自定义容器使用网桥模式。在同一个自定义网络中,容器名会被注册为服务名,该网络中的所有容器可以通过容器名互相访问,而不是通过固定 IP:

# 网桥模式启动 nginx
$ docker run -d -p 4900:80 --name=nginx00 nginx
$ docker run --name=alpine -it alpine sh
/ ping nginx00
ping: bad address 'nginx00'
/ exit

# 自定义网络模式启动 nginx
$ docker rm nginx00 alpine -f
$ docker network create my_net
$ docker run -d -p 4900:80 --network=my_net  --name=nginx00 nginx
$ docker run -it --network=my_net --name=alpine alpine sh
/ ping nginx00
PING nginx00 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.131 ms

可见,自定义网络内部维护了 IP 和服务名的对应关系。注意:使用-pports指定的端口映射是对外部网络的映射,自定义网络中的容器只能通过容器本身的端口访问,对于上方例子,Alpine 实例仍需通过 80 端口访问 Nginx 的 HTTP 服务。

命令

# 显示已有的网络,默认有桥接和主机模式以及无网络
$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
18c3158281eb   bridge    bridge    local
3c55b0dd5a24   host      host      local
421ed165570b   none      null      local

# 创建自定义网络,默认模式桥接,主机模式网络只允许存在一个
$ docker network create <网络名> [-d="模式名"]

# 删除自定义网络
$ docker network rm <网络名>

# 查看网络详细信息
$ docker network inspect <网络名>

# 删除所有无用网络
$ docker network prune

# 以指定网络运行容器
$ docker run --network <网络名[:参数]> <容器>

# 将一个容器连接至网络
$ docker network connect <网络> <容器>

# 将一个容器从网络断开
$ docker disconnect <网络> <容器>

Docker Compose

Docker-Compose 是由 Docker 官方提供的开源 Docker 容器集群编排工具。Docker-Compose 通过一个docker-compose.yml文件配置多个容器间的调用关系,管理多个容器以组成单个应用,即多个微服务组成单个云应用

Docker-Compose 要解决的需求:

  • 解决容器间的依赖关系和加载顺序问题;
  • 统一操作和管理容器实例。

概念

Docker 容器是微服务,Docker-Compose 则将微服务组装成一个云应用。Docker 容器本身只需要占用非常少的资源,对于一个采用 Docker 技术实现的微服务 Web 云应用,往往会包括:

  • Web 服务容器
  • 消息队列容器
  • 数据库容器
  • 服务发现注册中心容器
  • 负载均衡容器
  • 通用网关容器

Docker-Compose 将负责定义这些容器的交互过程,统一操作和管理容器,即容器编排。每个容器被称为服务(service),应用被称为工程(project),工程在配置文件docker-compose.yml中定义。

服务

工程

安装

  • Compose V2
  • GitHub 原始仓库
  • Gitee
# 安装 Compose V1,V1 作为一个单独程序存在
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose --version

# 卸载 V1
rm /usr/local/bin/docker-compose

# 安装 Compose V2,V2 是一个 docker 插件
mkdir -p /usr/local/lib/docker/cli-plugins
curl -SL https://gitee.com/mirrors/docker-composereleases/download/v2.4.1/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
docker compose version

# 卸载 V2
rm /usr/local/lib/docker/cli-plugins/docker-compose

命令

$ docker-compose -h # 查看帮助
$ docker-compose up # 启动所有服务
$ docker-compose up -d # 启动所有服务并在后台运行
$ docker-compose down # 停止所有服务,并删除容器、网络、卷、镜像
$ docker-compose exec <服务名> # 在服务容器中执行命令
$ docker-compose ps # 显示 compose 负责的所有容器
$ docker-compose top # 显示 compose 负责的所有容器进程
$ docker-compose logs [服务名] # 查看容器日志输出
$ docker-compose config # 检查配置文件并输出
$ docker-compose config -q # 检查配置文件,只输出问题项
$ docker-compose restart # 重启所有服务
$ docker-compose start # 启动所有服务
$ docker-compose stop # 停止所有服务

使用

  1. 为每个镜像编写 Dockerfile,并构建镜像;
  2. 使用docker-compose.yml编排工程;
  3. 执行docker-compose up运行工程。

Compose File

文档

version: "3" # 可选,指定适用的 compose 版本,不推荐用此字段进行版本选择,配置应该是向后兼容的

networks:
  # 创建网络,相当于 docker network create 命令
  demo_net:
    # 网络定义,可以没有属性


services:
  # 指定服务(容器实例),相当于配置 docker run 命令
  demo-client:
    # 为服务命名
    image: compose-demo-client # 服务使用的镜像
    container_name: demo-client # 容器名
    restart: always # 总是跟随 docker 重新启动
    ports:
      # 端口映射,可以有多个
      - 4700:80
    volumes:
      # 卷映射,可以有多个
      - .:/usr/share/nginx/html
    networks:
      # 网络映射,可以有多个
      - demo_net
    depends_on:
      # 依赖关系,该字段将确定容器开启顺序
      - demo-server
    privileged: true # 开启特殊权限,避免访问卷异常

  demo-server:
    image: compose-demo-server
    container_name: demo-server
    ports:
      - 4444:4444
    networks:
      - demo_net
    environment:
      # 环境变量
      NODE_ENV: "production"
    # 命令,相当于 Dockerfile 中的 CMD 和 docker run 中的命令参数,覆盖之前的 CMD
    # 在有 ENTRYPOINT 的情况下会成为其参数
    command: node index.js

Docker 监控和记录

在日常运维工作中需要时刻监控和记录容器状态,Docker 本身提供了简单的docker stats命令查看状态,但只能监控本机,并且无法保存日志记录和预警。

简单的运维工作可以使用 Portainer 简化流程,而实际的监控和记录工作则需要组合使用 CAdvisor,InfluxDB 和 Granfana 三种工具进行。

对于大规模和超大规模分布式集群,则更适合使用 Kubernetes 作为整套解决方案。

Portainer

Portainer 是一款轻量级的 Docker 图形化管理工具。

安装

文档

Portainer for Docker 作为服务被打包为 Docker 镜像,直接安装镜像并运行即可:

$ docker run -d -p 18000:8000 -p 18001:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
# --restart=always 跟随 docker 服务一同重启

使用

Portainer 是一个 Web 管理面板,访问服务器对应端口即可使用,默认端口为 9000 。

CIG

CIG 指使用 CAdvisor,InfluxDB 和 Grafana 实现的一套容器监控方案。这套方案较老,CAdvisor 已不进行积极更新,无法很好适配新版的 InfluxDB 。一般企业里会有自己的监控方案,学习搭建 CIG 可以初步了解 Docker 运行时的日志数据流动模型。

CAdvisor 由 Google 使用 Go 开发。CAdvisor 有强大的数据收集功能,提供基本的 Web 监视。由于默认只能存储 2 分钟内的数据,并且只支持单机搜集,需要配合数据库系统使用。

InfluxDB 由 Influx Data 使用 Go 开发。InfluxDB 是一种用于时序、事件和指标记录的领域型数据库,CAdvisor 提供了对其的集成。

Grafana 使用 TypeScript 实现,提供基于 Web 的可视化监控平台,适用于多种数据库的图形化监控显示。

安装

CIG 适合使用 Docker-Compose 安装为一组服务。编写docker-compose.yml

version: "3"

networks:
  container_overwatch:

volumes:
  grafana_data: {}

services:
  influxdb:
    container_name: influxdb
    image: tutum/influxdb:0.9 # 由于 CAdvisor 不支持最新的 2.x,只能使用已经废弃的 influxdb 版本 
    restart: always
    environment:
      - PRE_CREATE_DB=cadvisor
      - ADMIN_USER=root
      - INFLUXDB_INIT_PWD=asusual # 记得更改,之后重置较为繁琐,建议配置环境变量时就确定
    ports:
      - 10236:8083 # Web UI 端口
      - 8086:8086 # 数据库端口,因为作者在 Web UI 中通过本地浏览器远程访问该端口(按道理说应该通过后端服务转发),并且代码写死了,更改会导致 Web UI 中的数据查询功能不可用
    volumes:
      - ./data/influxdb:/data
    privileged: true
    networks:
      - container_overwatch

  cadvisor:
    container_name: cadvisor
    image: google/cadvisor
    # -storage_driver_db 的值要和上方配置的初始数据库名称相同
    # -storage_driver_host 对应服务名和数据库接口端口,注意是容器端口而不是宿主机映射后端口
    command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxdb:8086
    restart: always
    ports:
      - "10234:8080" # Web UI
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    privileged: true
    networks:
      - container_overwatch

  grafana:
    container_name: grafana
    user: root
    image: grafana/grafana
    restart: always
    ports:
      - "10235:3000" # Web UI
    volumes:
      - ./data/grafana:/var/lib/grafana
    environment:
      - HTTP_USER=admin
      - HTTP_PASS=admin # 记得更改,进入 Web GUI 后也可重置
    privileged: true
    networks:
      - container_overwatch

配置 Grafana

Granfana 的配置过程暂时无法完全脚本化,需要使用 Web UI 进一步设置。

  1. 选择数据源:

找到数据源配置
Docker使用入门_第2张图片
2. 配置数据源:
Docker使用入门_第3张图片
Docker使用入门_第4张图片
3. 在菜单中找到创建监控面板的功能,具体步骤可以参考 Granfana 文档。

了解 Kubernetes

Kubernetes 简称 “K8s”(8 替代中间 8 个字符),是一款开源的适用于大规模和超大规模分布式云容器集群运维解决方案,不仅仅是 PaaS 。

Kubernetes 的特性:

  • 服务发现:通过域名或 IP 地址公开容器,自动检测服务状态,避免访问失效的提供者;
  • 负载均衡:分配流量,稳定部署;
  • 存储编排:编排和自动运行容器并为其分配内存和外存空间,根据容器运行状态自动调整和删除以提高空间利用效率;
  • 自动部署和回滚:根据用户描述自动控制容器和选择部署目标,在当前版本出现问题时快速回滚并运行;
  • 自动完成装箱计算:根据用户描述和宿主状态自动进行部署和管理决策,避免容器内存和 CPU 异常;
  • 自我修复:自动重启、替换和终止问题容器;
  • 密钥和配置管理:管理所有包括 SSH Keygen 、OAuth 在内的敏感和配置信息。

如果你是后端工程师或运维人员,一定要尝试下 k8s 。

附:常用软件镜像命令

Node.js

docker pull node:16
docker run -it -p 3600:3000 --privileged=true --name=node \
-v /root/docker-node/nodedemo:/root/ \
node:16 bash
npm i -g cnpm --registry=http://registry.npm.taobao.org
cd /root
cnpm i
nohup node app.js > node.log 2>&1 &

curl -XGET your_service_name:3600

MySQL

docker pull mysql:5.7
docker run -p 4300:3306 -e MYSQL_ROOT_PASSWORD=test -d --name=mysql mysql:5.7 # 开启 mysql 并设置密码
docker exec -it <容器 id> bash
mysql -uroot -p

MySQL 镜像的默认数据库编码为拉丁编码,为了能够使用中文需要特别设置:

> show variables like "character%";
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     |
| character_set_connection | latin1                     |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | latin1                     |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

此外,为了避免删除容器导致数据丢失,使用数据卷:

docker run -d -p 4300:3306 --privileged=true \
-v /root/docker-mysql/log:/var/log/mysql \
-v /root/docker-mysql/data:/var/lib/mysql \
-v /root/docker-mysql/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=test \
--name=mysql \
mysql:5.7

设置容器卷后修改编码:

$ cd /root/docker-mysql/conf
$ vi my.cnf
[client]
default_character_set=utf8
[mysqld]
collation_server=utf8_general_ci
character_set_server=utf8
$ docker restart mysql

之后如果删除了容器,只要使用同样的映射还可访问数据库中的数据。

Redis

docker pull redis:6
docker run -d -p 4500:6379 --name=redis redis:6
docker exec -it redis bash
redis-cli

为了避免数据丢失,使用数据卷。首先编辑配置文件:

$ vi /root/docker-redis/redis.conf

# 找到 requirepass,设置密码
requirepass test
# 允许外网连接,注释 bind
# bind 127.0.0.1
# 关闭守护模式,守护模式会和 docker -d 选项冲突
daemonize no
# 开启数据持久化,可选
appendonly yes
# 关闭保护模式,可选,影响 bind 是否生效
protected-mode no

开启卷运行:

$ docker run -d -p 4500:6379 --name=redis --privileged=true \
-v /root/docker-redis/redis.conf:/etc/redis/redis.conf \
-v /root/docker-redis/data:/data \
redis:6 redis-server /etc/redis/redis.conf
# 注意附加命令,指示容器内 redis server 读取指定配置文件

$ docker exec redis bash
$ redis-cli
$ auth test
$ ping
PONG

Nginx

docker pull nginx
docker run -d -p 3500:80 --name=nginx nginx
curl your_service_name:3500

要部署容器外的静态资源,使用卷挂载:

docker run -d -p 3500:80 --privileged=true --name=nginx \
-v /www/wwwroot/your_site:/usr/share/nginx/html \
nginx
curl your_service_name:3500

MongoDB

docker pull mongo
docker run -d --name mongo \
-e MONGO_INITDB_ROOT_USERNAME=root \
-e MONGO_INITDB_ROOT_PASSWORD=test \
-p 4600:27017 \
-v /root/docker-mongo/data:/data/db \
mongo --auth

docker exec -it mongo bash

# 登录,使用 admin 命令进入 admin 数据库验证,或在进入后 use admin
mongo admin
db.auth("root","test")

附:Dockerfile 举例

Nginx

FROM nginx
COPY . /usr/share/nginx/html
COPY   nginx.conf /etc/nginx/nginx.conf
WORKDIR /usr/share/nginx/html
CMD ["nginx","-g","daemon off;"]

FFmpeg

FFmpeg 需要从源代码编译,本例中使用 CentOS 7 作为基础镜像,最后的镜像会比较大。一般在生产环境中会选择 Alpine 作为基础镜像。

FROM centos:7

RUN yum -y install git
RUN yum -y install centos-release-scl
RUN yum -y install devtoolset-9 
RUN yum -y install nasm
WORKDIR /opt
RUN git clone https://gitee.com/mirrors/ffmpeg.git
WORKDIR /opt/ffmpeg
RUN git checkout -B release-4.4 origin/release/4.4
RUN ./configure --enable-shared --disable-static --prefix=/usr/local --enable-gpl --enable-version3 --enable-nonfree
RUN make -j$(nproc)
RUN make install
RUN echo export LD_LIBRARY_PATH=/usr/local/lib/ | tee -a ~/.bashrc
RUN source ~/.bashrc
WORKDIR /opt
RUN rm -rf ./ffmpeg
RUN yum -y remove git
RUN yum -y remove centos-release-scl
RUN yum -y remove devtoolset-9 
RUN yum -y remove nasm

Vite TS Vue 3

FROM node:16

ENV NODE_ENV production
ENV PROJECT_NAME vue3demo
ENV PROJECT_DIR /wwwroot/${PROJECT_NAME}
ENV NPM_INSTALL cnpm
ENV NPM_REPO http://registry.npm.taobao.org
ENV DEV_PORT 3000

VOLUME $PROJECT_DIR

WORKDIR $PROJECT_DIR
RUN npm install -g $NPM_INSTALL --registry=https://registry.npm.taobao.org
RUN $NPM_INSTALL install vue@next --save
RUN $NPM_INSTALL install vite typescript vue-tsc @vitejs/plugin-vue -D
RUN chmod a+rwx $PROJECT_DIR
EXPOSE $DEV_PORT

CMD $NPM_INSTALL install && npm run dev

运行:

docker run -p -d 4200:3000 --privileged=true  --name=vue -v /root/docker-node/vue3demo:/wwwroot/vue3demo vue3demo

你可能感兴趣的:(后端,docker,运维,容器)