Nginx、Docker、Docker Compose与Redis缓存
Nginx
Nginx(“engine x”)是一个
高性能的HTT
P和反向代理服务器
,它具有高并发、高性能、扩展性好、异步非阻塞的事件驱动模型等特点,在互联网行业被广泛应用,使用Nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等
Nginx有三大应用场景
- 静态资源服务-通过本地文件系统提供服务
- 反向代理服务-缓存、负载均衡
API
服务-openresty
nginx.conf配置文件
-
全局块
:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等 -
events块
:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等 -
http块
:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等 -
server块
:配置虚拟主机的相关参数,一个http中可以有多个server -
location块
:配置请求的路由,以及各种页面的处理情况
user nginx; # 运行用户,默认即是nginx,可以不进行设置
worker_processes 1; # Nginx 进程数,一般设置为和 CPU 核数一样
error_log /var/log/nginx/error.log warn; # Nginx 的错误日志存放目录
pid /var/run/nginx.pid; # Nginx 服务启动时的 pid 存放位置
events {
use epoll; # 使用epoll的I/O模型(如果你不知道Nginx该使用哪种轮询方法,会自动选择一个最适合你操作系统的)
worker_connections 1024; # 每个进程允许最大并发数
}
http { # 配置使用最频繁的部分,代理、缓存、日志定义等绝大多数功能和第三方模块的配置都在这里设置
# 设置日志模式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main; # Nginx访问日志存放位置
sendfile on; # 开启高效传输模式
tcp_nopush on; # 减少网络报文段的数量
tcp_nodelay on;
keepalive_timeout 65; # 保持连接的时间,也叫超时时间,单位秒
types_hash_max_size 2048;
include /etc/nginx/mime.types; # 文件扩展名与类型映射表
default_type application/octet-stream; # 默认文件类型
include /etc/nginx/conf.d/*.conf; # 加载子配置项
server {
listen 80; # 配置监听的端口
server_name localhost; # 配置的域名或IP
location / {
root /usr/share/nginx/html; # 网站根目录
index index.html index.htm; # 默认首页文件
deny 172.168.22.11; # 禁止访问的ip地址,可以为all
allow 172.168.33.44; # 允许访问的ip地址,可以为all
}
error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面
error_page 400 404 error.html; # 同上
}
}
Nginx 常用操作命令
start nginx
:开启服务nginx -s stop | nginx -s quit
:停止服务nginx -s reload
:重启服务kill -QUIT
:从容停止Nginx 主进程号kill -TERM
:快速停止Nginx主进程号./nginx -t
:检查配置文件是否有语法操作./nginx -t -c /usr/local/nginx/conf/nginx.conf
:显示指定配置文件nginx -v
:查看版本- 找对应pid有两种方法
1.使用命令 cat /var/run/nginx.pid
2.直接 ps -ax | grep nginx 找到master process(主线程)的Id
然后执行Linux的杀死线程命令:
sudo kill -s QUIT xxxx
Docker
Docker 是一个开源的应用容器引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在本地编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机)、bare metal、OpenStack 集群和其他的基础应用平台。
简单的理解,Docker类似于集装箱,各式各样的货物,经过集装箱的标准化进行托管,而集装箱和集装箱之间没有影响。也就是说,Docker平台就是一个软件集装箱化平台,这就意味着我们自己可以构建应用程序,将其依赖关系一起打包到一个容器中,然后这容器就很容易运送到其他的机器上进行运行,而且非常易于装载、复制、移除,非常适合软件弹性架构。
因此,就像船只、火车或卡车运输集装箱而不论其内部的货物一样,软件容器充当软件部署的标准单元,其中可以包含不同的代码和依赖项。 按照这种方式容器化软件,开发人员和 IT 专业人员只需进行极少修改或不修改,即可将其部署到不同的环境。
总而言之,Docker 是一个开放平台,使开发人员和管理员可以在称为容器的松散隔离的环境中构建镜像、交付和运行分布式应用程序。以便在开发、QA 和生产环境之间进行高效的应用程序生命周期管理。
Docker 架构
- Docker 包括三个基本概念:
-
镜像(Image)
:Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。 -
容器(Container
:镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。 -
仓库(Repository)
:仓库可看成一个代码控制中心,用来保存镜像。
-
Docker的应用场景
- Web 应用的自动化打包和发布。
- 自动化测试和持续集成、发布
- 在服务型环境中部署和调整数据库或其他的后台应用
- 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境
Docker 的安装
Docker阿里镜像加速源
加速器地址:[系统分配前缀].mirror.aliyuncs.com
- 登录容器镜像服务控制台,在左侧导航栏选择镜像工具 > 镜像加速器,在镜像加速器页面获取加速器地址。
- 尝试运行一个
Nginx服务器
docker run -d -p 80:80 --name webserver nginx
Docker常用命令
-
docker --help
: 看 docker 总体帮助文档 -
docker info
:查看 docker 概要信息 -
docker version
:查看 Docker 版本信息 -
docker login [OPTIONS] [SERVER]
:登录远程仓库
docker login -u myusername -p mypassword mydockerregistry.com
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
:打标签
docker tag myimage:latest mydockerregistry.com/myimage:latest
docker push [OPTIONS] NAME[:TAG]
:推送镜像
docker push mydockerregistry.com/myimage:latest
docker inspect 容器ID或容器名
:查看容器详情docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
: 获取镜像
docker pull ubuntu:18.04
docker run [options] image [command][arg...]
: 启动容器- options
--name=
容器新名字 为容器指定一个名称,用来区分容器-it
:交互式(i)终端(t)--rm
:容器退出后随之将其删除,可以避免浪费空间-h "mars"
: 指定容器的hostname;-P
: 随机端口映射,大写 P-p
: 指定端口映射,小写 p-d
: 后台运行容器,并返回容器ID-e username="ritchie"
: 设置环境变量--env-file=[]
: 从指定文件读入环境变量--volume , -v
: 绑定一个卷--net="bridge"
: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型--network
mynetwork 加入网段
- options
docker run -it --rm ubuntu:18.04 bash
# `ubuntu:18.04`:这是指用 `ubuntu:18.04` 镜像为基础来启动容器
# `bash`:放在镜像名后的是 **命令**,这里我们希望有个交互式 Shell,因此用的是 `bash`
docker run --name webserver -d -p 80:80 nginx
docker run -d --name mysql-container -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 mysql:8.0
docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
:创建新容器但不启用
docker create --name vhen_Nginx001 nginx:latest # 使用docker镜像nginx:latest创建一个容器,并将容器命名为vhen_Nginx001
docker build [OPTIONS] PATH | URL | -
:用于使用Dockerfile
创建镜像–build-arg=[]
: 设置镜像创建时的变量–cpu-shares
: 设置 cpu 使用权重;–cpu-period
: 限制 CPU CFS周期;–cpu-quota
: 限制 CPU CFS配额;–cpuset-cpus
: 指定使用的CPU id;–cpuset-mems
: 指定使用的内存 id;–disable-content-trust
: 忽略校验,默认开启;-f
: 指定要使用的Dockerfile路径;–force-rm
: 设置镜像过程中删除中间容器;–isolation
: 使用容器隔离技术;*–label=[]
: 设置镜像使用的元数据;*-m
: 设置内存最大值;–memory-swap
: 设置Swap的最大值为内存+swap,"-1"表示不限swap;–no-cache
: 创建镜像的过程不使用缓存;–pull
: 尝试去更新镜像的新版本;–quiet, -q
: 安静模式,成功后只输出镜像 ID;–rm
: 设置镜像成功后删除中间容器;–shm-size
: 设置/dev/shm的大小,默认值是64M;–ulimit
: Ulimit配置。–squash
: 将 Dockerfile 中所有的操作压缩为一层。–tag, -t
: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。–network
: 默认 default。在构建期间设置RUN指令的网络模式
docker build -t vhen/ubuntu:v1 # 使用当前目录的Dockerfile创建镜像
docker build github.com/creack/docker-firefox # 使用URL 的 Dockerfile 创建镜像
-
docker exec -it nginx bash
: 进入容器-it
表示 可交互的终端bash
表示使用命令行进行交互
-
exit
退出容器 -
docker ps [options]
: 列出当前所有正在运行的容器-a
: 列出当前所有正在运行的容器和历史运行过的-f
:根据条件过滤显示的内容-l
: 显示最近创建的容器-n
: 显示最近n个创建的容器-q
: 静默模式,只显示容器编号-s
:显示总的文件大小--format
:指定返回值的模板文件--no-trunc
:不截断输出
-
docker logs 容器ID或容器名
:查看容器日志 -
docker top 容器ID或容器名
:查看容器内运行的进程,支持 ps 命令参数 -
docker cp 容器ID:容器内路径 目的主机路径
: 从容器内拷贝文件到主机上
docker cp nginx:/www /tmp/ #将nginx容器的/www 拷贝到本地/tmp下
-
docker start 容器ID或容器名
: 启动已停止运行的容器 -
docker stop 容器ID或容器名
:停止容器 -
docker restart 容器ID或者容器名
: 重启容器 -
docker pause 容器ID或者容器名
:暂停容器中所有的进程 -
docker unpause
:恢复容器中所有的进程 -
docker rm [options] 容器名称|容器ID
:删除指定容器-f
:强制删除一个运行中的容器-l
:移除容器间的网络连接,而非容器本身-v
: 删除与容器关联的卷
docker rm -f $(docker ps -qa) # 删除所有停止的容器
docker search [OPTIONS] 镜像名称
:搜索镜像名称--filter , -f
:基于给定条件过滤输出。--format
:使用模板格式化显示输出。--limit
:Max number of search results ,默认值25。--no-trunc
:禁止截断输出。
docker search -f stars=10 redis
-
docker images [options]
:列出本地主机上的镜像-a
:列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层)--digests
:显示镜像的摘要信息-f
:显示满足条件的镜像is-official=true|false
:搜索官方镜像或非官方镜像is-automated=true|false
:搜索自动构建的镜像或非自动构建的镜像stars=xxx
:搜索星级大于等于指定值的镜像name=xxx
:根据镜像名称进行精确匹配搜索description=xxx
:根据镜像描述进行模糊匹配搜索
-q
: 只显示镜像ID--format
:指定返回值的模板文件--no-trunc
:显示完整的镜像信息
-
docker image ls
列出镜像- 列表包含了
仓库名
、标签
、镜像 ID
、创建时间
以及所占用的空间
- 列表包含了
docker image ls -f since=ubuntu:18.04
# 过滤器参数 --filter,或者简写 -f,看到在 ubuntu:18.04 之后建立的镜像
# 想查看某个位置之前的镜像也可以,只需要把 `since` 换成 `before` 即可
docker image ls -q
# -q 指定范围的 ID 列表
docker image rm
删除本地镜像<镜像>
可以是镜像短 ID
、镜像长 ID
、镜像名
或者镜像摘要
docker image rm [选项] <镜像1> [<镜像2> ...]
-
docker rmi [image]
:删除不需要的镜像- 删除单个:
docker rmi -f 镜像ID
- 删除多个:
docker rmi -f 镜像名1:TAG 镜像名2:TAG
- 删除全部:
docker rmi -f $(docker images -qa)
- 删除单个:
-
docker system df
查看镜像、容器、数据卷所占用的空间 -
其他命令自行官方查找
Dcokerfile 构建镜像
Dockerfile是一个文本文件,文件中包含了一条条指令(instrucation),用于构建镜像。每一条指定构建一层镜像,因此每一条指令的内容,就是描述该层镜像应当如何构建。
FROM:为构建流程指定基础镜像;必须是第一条指令
,一切都是从这里开始的
tag
或digest
是可选项,如果不指定,会使用 latest 版本作为基础镜像
# 语法
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
# 示例
FROM redis
FROM redis:7.0.5
FROM redis@7614ae9453d1
RUN:用于在构建镜像过程中执行命令
# 语法
RUN <command>
RUN ["executable", "param1", "param2"]
# 示例
RUN echo "Hello Dockerfile"
RUN ["echo", "Hello Dockerfile"]
# 多个推荐写法
RUN apt-get update && apt-get install -y \
aufs-tools \
automake \
build-essential \
curl \
dpkg-sig \
libcap-dev \
libsqlite3-dev \
mercurial \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
s3cmd=1.1.* \
&& rm -rf /var/lib/apt/lists/*
WORKDIR:指定后续指令的工作目录,类似于 Linux 中的 cd 命令
# 语法
WORKDIR /path/to/workdir
# 示例
# 当前工作目录为 /a/b/c
WORKDIR /a
WORKDIR b
WORKDIR c
# 使用 Dockerfile 中设置的环境变量
ENV DIR_PATH=/demo
WORKDIR $DIR_PATH/$DIR_NAME # 未显示指定,直接忽略
RUN pwd
ADD:将构建上下文中指定目录下的文件复制到镜像文件系统的指定位置
- 路径必须位于构建的上下文中; 你不能添加../something / something,因为docker构建的第一步是将上下文目录(和子目录)发送到docker守护进程
--chown
功能仅用于Dockerfile构建linux容器.在windows容器上无效- 目标位置下的某些目录不存在,会自动创建
- src
支持通配符
,主要是 * 和 ?,通过 Go 语言的 filepath.Match 规则进行匹配 - 如果 src 包含多个文件资源,或者使用了通配符,dest 必须是文件夹,即 dest 以 / 结尾
某一 ADD 指令对应的 src 资源有变更,Dockerfile 中这条指令后的构建缓存都会失效
- src 支持设置多个文件资源,每个文件资源都会被解析为构建上下文中的相对路径
- 如果是可识别的压缩格式(identity,gzip,bzip2或xz)的本地tar存档,则将其解压缩为目录。 远程URL中的资源不会被解压缩。 复制或解压缩目录时,它与tar -x具有相同的行为
- 如果是URL且不以尾部斜杠结尾,则从URL下载文件并将其复制到
- 如果是URL并且以尾部斜杠结尾,则从URL推断文件名,并将文件下载到
<dest> /<filename>
。 例如,ADD http://example.com/foobar /
将创建文件/foobar
。 URL必须具有非常重要的路径,以便在这种情况下可以发现适当的文件名
# 语法
ADD [--chown=<user>:<group>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] # 路径中含有空格的情况,可以使用第二种方式
ADD <git ref> <dir> # 处于实验阶段的特性,直接添加 Git 资源到镜像文件系统中
# 示例
ADD demo.txt /dest_dir/demo.txt # 将 demo.txt 复制到 /dest_dir 下的 demo.txt
ADD demo* /dest_dir/ # 将以 demo 开头的文件复制到 /dest_dir 下
ADD dem?.txt /dest_dir/ # ? 被一个单字符替换,可以是 demo.txt, 将符合条件的文件复制到 /dest_dir 下
ADD test/ /dest_dir/ # 将 test 文件夹下的所有文件都复制到 /dest_dir/ 下
ADD test/ dest_dir/ # 将 test 文件夹下的所有文件都复制到 <WORKDIR>/dest_dir/ 下
ADD http://example.com/a/url.txt / # 从 URL 下载 url.txt 文件,另存为文件 /a/url.txt,其中文件夹 /a/ 是自动创建的
# 假设文件名为 demo[0].txt, 其中含有特殊符号 [ 和 ]
ADD demo[[]0].txt /dest_dir
COPY:功能和语法与 ADD 类似,但是不会自动解压文件,也不能访问网络资源
# 语法
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
# 示例
# 通配符
COPY hom* /mydir/ # adds all files starting with "hom"
COPY hom?.txt /mydir/ # ? is replaced with any single character, e.g., "home.txt"
# 绝对路径,或相对于WORKDIR的路径
COPY test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/ # adds "test" to /absoluteDir/
CMD:构建镜像成功后,所创建的容器启动时执行的命令,常与 ENTRYPOINT 结合使用
- 一个Dockerfile文件中,只能有一个CMD指令,如果出现多个,只有最后一个起作用; 但可以使用
&&
连接多个命令
# 语法 使用双引号,而不是单引号
CMD command param1 param2 # shell 方式
CMD ["executable","param1","param2"] # exec 方式,这是推荐的方式
CMD ["param1","param2"] # 作为 ENTRYPOINT 的默认参数,这种方式是 exec 方式的特殊形式
# 示例
CMD echo "Hello Dockerfile"
CMD ["echo", "Hello Dockerfile"]
FROM node
CMD ["/usr/bin/wc", "--help"]
ENTRYPOINT:用于配置容器以可执行的方式运行,常与 CMD 结合使用
- Dockerfile 中只有最后一条 ENTRYPOINT 指令生效
- exec形式的ENTRYPOINT中的命令行参数将会追加到
docker run <image>
所有元素的后面。并且会覆盖CMD指令指定的参数。这允许将参数传递给入口点。docker run <image> -d
会将-d参数传递给入口点。 您可以使用docker run --entrypoint
标志覆盖ENTRYPOINT指令
# 语法 要使用双引号,而不是单引号
ENTRYPOINT ["executable", "param1", "param2"] # 推荐方式
ENTRYPOINT command param1 param2
# 示例
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
# 或
FROM ubuntu
ENTRYPOINT exec top -b
ENV:设置环境变量
# 语法
ENV <key>=<value> ...
ENV <key> <value>
# 示例
ENV MY_NAME="vhen4646" MY_CAT="nginx" # 设置多个环境变量,支持引号以及反斜杠作为续行符号
ENV MY_CAT nginx # 不推荐这种写法
# 查看最终镜像中的环境变量
docker image inspect -f='{{json.ContainerConfig.Env}}' image_name
EXPOSE:约定容器运行时监听的端口,通常用于容器与外界之间的通信
- 支持
TCP
或者UDP
协议,如果不显式指定协议,默认使用 TCP 协议 - EXPOSE 并不会真正将端口发布到宿主机,而是作为一种约定,让镜像使用者在运行容器时,用
-p
分别发布约定端口,或者-P
发布所有约定端口
#语法
EXPOSE <port> [<port>/<protocol>...]
#示例
EXPOSE 8080
EXPOSE 80/tcp
EXPOSE 80/udp
EXPOSE 9090/tcp 9090/udp
USER:指定当前构建阶段以及容器运行时的默认用户,以及可选的用户组
# 语法
USER <user>[:<group>]
USER <user>[:<GID>]
USER <UID>[:<GID>]
USER <UID>[:<group>]
# 示例
# 用 USER 指定用户后,Dockerfile 中在此之后的 RUN, CMD, ENTRYPOINT 指令都会使用这个用户
#此外,这个用户也是容器运行时的默认用户
USER vhen864
USER vhen864 :vhenGroup
# 在 Windows 系统中指定非内置用户时,需要先用 **net user /add** 命令创建用户
FROM microsoft/windowsservercore
RUN net user /add my_user # 在容器中创建 Windows 用户
USER my_user # 设置后续指令的用户
VOLUME:创建具有指定名称的挂载数据卷,用于数据持久化
- 数据持久化,避免容器重启后丢失重要数据
- 修改数据卷时不会对容器产生影响,防止容器不断膨胀
- 有利于多个容器共享数据
# 语法
VOLUME ["volume1", "volume2", ...] # 必须使用双引号,而不是单引号
VOLUME volume1 volume2 ...
# 示例
# 定义 VOLUME 后,Dockerfile 的后续指令对该数据卷所做的修改操作会被丢弃
VOLUME ["/demo/data", "/demo/logs"]
VOLUME /myvol
LABEL:为镜像添加元数据
# 请确保使用双引号,而不是单引号。特别是当你使用字符串插值时(例如LABEL示例="foo-$ENV_VAR"),
# 单引号将按原样取字符串,而不拆包变量的值
LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 示例
# 同一条 LABEL 指令可以指定多个元数据,用空格分开,可以在同一行,也可以用反斜杠分割,放在多行
LABEL author="Jason 315" version="1.0.0" description="Dockerfile案例"
LABEL author="Jason 315" \
version="1.0.0" \
description="Dockerfile案例"
# 可以用如下命令查看元数据
docker image inspect -f='{{json.ContainerConfig.Labels}}' image_name
Vue使用Docker和Nginx部署实战
- 在根目录下创建
nginx.conf
与Dockerfile
文件
- nginx.conf
# 监听 80 端口
server {
#监听端口
listen 80;
# 域名或ip
server_name localhost;
#编码格式
charset utf-8;
# 前端静态文件资源
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ @rewrites;
}
# 配置如果匹配不到资源,将url指向 index.html, 在 vue-router 的 history 模式下使用,就不会显示404
location @rewrites {
rewrite ^(.*)$ /index.html last;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
- Dockerfile
# node镜像
FROM node:20-alpine as build-stage
WORKDIR /my-app
ADD package*.json ./
RUN npm install --registry=https://registry.npm.taobao.org \
&& npm ci --quiet
ADD ./ .
RUN npm run build
# nginx镜像
FROM nginx:alpine as prod-stage
RUN mkdir /my-app
COPY --from=build-stage /my-app/dist /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/conf.d/default.conf
- 构建镜像
docker build -t my-app:latest .
- 运行镜像
docker run --name my-app-test -d -p 8080:80 my-app:latest
Docker Compose
Docker Compose是一个用于定义和运行多容器 Docker 应用程序的工具。通过Docker Compose ,可以使用 docker-compose.yml文件(YAML 格式)来配置应用程序相关联的容器,包含多个服务services;每个服务中定义了创建容器时所需的镜像、端口映射、参数、依赖、环境变量等。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务,可以让它快速高效地部署多个容器了。这种方法特别适用于开发、测试、部署和扩展复杂应用程序;一旦您有了Compose文件,您就可以用一个命令创建并启动您的应用程序:docker Compose-up
-
安装的是图形界面的桌面版本,就不用安装Docker compose,桌面版自带。 命令行输入
docker compose version或者docker-compose -v
可以查看docker compose的版本信息:
Docker Compose 用途
-
简化多容器应用的配置
- 使用一个 YAML 文件(通常命名为
docker-compose.yml
)来定义应用中的所有服务(容器),包括它们的依赖、环境变量、暴露的端口等 - 适用于需要多个服务协同工作的应用,如前端、后端和数据库服务。
- 使用一个 YAML 文件(通常命名为
-
一致的开发、测试和生产环境、部署的便利性
- 适用于开发者在本地环境进行开发和测试
- 有助于在本地环境中模拟生产环境,提高开发和测试的效率
- Docker Compose 确保应用在开发、测试和生产环境中以相同的方式运行,减少环境差异带来的问题
- 便于运维人员在生产环境中部署和管理复杂的应用堆栈
-
自动化和持续集成
- 支持在持续集成管道中运行自动化测试
- 可以轻松集成到 CI/CD 流程中,自动化构建、测试和部署应用
-
微服务架构
- 对于基于微服务架构的应用,Docker Compose 提供了一种管理多个微服务的简单方法,每个服务可以在独立的容器中运行
-
快速部署和伸缩
- 支持服务的水平扩展,可以轻松地增加或减少服务的实例数
- 快速启动和停止整个应用或应用的特定服务
-
网络和卷的简化管理
- 管理数据卷,用于在容器之间共享数据或持久化数据
- 自动配置容器间的网络连接
docker compose常用命令
docker-compose up
:启动服务。默认情况下,它会运行docker-compose.yml
文件中定义的所有服务
docker-compose upc # 启动所有服务
docker-compose up -d # 在后台运行服务
docker-compose up --build # 强制重新构建服务
docker-compose down
:停止并移除由docker-compose up
创建的容器、网络、卷和默认镜像
docker-compose down # 停止并移除服务
docker-compose down -v #停止服务并移除卷
docker-compose logs
:查看服务的日志输出
docker-compose logs # 查看所有服务日志
docker-compose logs -f # 跟踪日志输出
-
docker-compose ps
:列出项目中当前状态的容器。 -
docker-compose exec
:在运行中的容器上执行命令
docker-compose exec service_name /bin/sh # 进入容器的交互式命令行
docker-compose exec service_name ls -l # 执行特定命令
-
docker-compose stop
:停止服务,但不移除容器 -
docker-compose start
:启动已停止的服务 -
docker-compose restart
:重启服务 -
docker-compose pause
:暂停服务 -
ocker-compose unpause
:恢复web容器 -
docker-compose pull
:拉取服务依赖的镜像 -
docker-compose push
:推送本地镜像到 Docker 镜像仓库 -
docker-compose build
:构建或重新构建服务 -
docker-compose rm
:移除已停止的容器 -
docker-compose top
:查看各个服务容器内运行的进程 -
docker-compose version
:显示 Docker Compose 的版本号
Docker Compose 文件
version
版本:指定 Compose 文件格式的版本。版本决定了可用的配置选项services
服务:定义了应用中的每个容器(服务)。每个服务可以使用不同的镜像、环境设置和依赖关系image
镜像:从指定的镜像中启动容器,可以是存储仓库、标签以及镜像 IDcontainer_name
容器名称:自定义容器名字build
构建:指定构建镜像的 dockerfile 的上下文路径,或者详细配置对象。,用于构建镜像expose
暴露端口:暴露端口给-link或处于同一网络的容器,不暴露给宿主机ports
端口:映射容器和宿主机的端口depends_on
依赖:依赖配置的选项,意思是如果 服务启动是如果有依赖于其他服务的,先启动被依赖的服务,启动完成后在启动该服务environment
环境变量:设置服务运行所需的环境变量env_file
环境变量文件: 使用环境变量.env文件restart
重启::控制容器的重启策略。在容器退出时,根据指定的策略自动重启容器no
:不自动重启always
:无论退出状态码如何,总是重启容器on-failure
:仅在容器非正常退出时(退出状态码非零)重启unless-stopped
:除非手动停止,否则总是重启
command
命令:覆盖容器启动后默认执行的命令。在启动服务时运行特定的命令或脚本,常用于启动应用程序、执行初始化脚本等networks
网络 定义了容器间的网络连接volumes
卷:用于数据持久化和共享的数据卷定义。常用于数据库存储、配置文件、日志等数据的持久化
Docker Engine 与 docker-compose version 之间的有以下关系 前往官网version
Nest项目使用Docker Compose部署实战
- package.json 配置
"start:prod": "npm run build && cross-env NODE_ENV=prod node dist/src/main",
- 在根目录创建
Dockerfile
文件
# node镜像
FROM node:20.8.1-alpine as my-blog-builder
WORKDIR /blog
ADD package*.json ./
RUN npm install --registry=https://registry.npm.taobao.org \
&& npm install
ADD ./ .
CMD npm run start:prod
EXPOSE 9000
- 在根目录下创建
docker-compose.yml
version: '3.8'
services:
blog:
container_name: blog-server
image: blog:latest
build: .
environment:
TZ: Asia/Shanghai
ports:
- 9000:9000
# restart: always
depends_on: # web服务依靠mysql要先等mysql启动
- db
# - my-redis
links:
- db:mysql
# 不声明的话,也会在同一个网络中,名称默认是 项目_default, 比如我这个项目叫 nest_vhen_blog, 默认的网络名称就是 nest_vhen_blog_default
networks:
- blog-network
db:
container_name: db-server
image: mysql:8
restart: on-failure
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: blog
MYSQL_USER: vhen
MYSQL_PASSWORD: 123456
MYSQL_ROOT_HOST: '%'
ports:
- 3307:3306
privileged: true #设置为true,不然数据卷可能挂载不了,启动不起
volumes:
# 数据logs
# - /usr/local/mysql/log:/var/log/mysql
- ./db/mysql-files:/var/lib/mysql-files
- ./db/logs:/var/log/mysql # 映射日志目录,将容器/var/log/mysql目录下的数据,备份到主机的 /db/log目录下
# 数据数据
- ./db/data:/var/lib/mysql # 映射数据目录,将容器/var/lib/mysql目录下的数据,备份到主机的 /db/data目录下
- ./db/conf:/etc/mysql # 映射配置目录,将容器/etc/mysql目录下的数据,备份到主机的 db/conf目录下
- ./db/init:/docker-entrypoint-initdb.d/ # 存放初始化的脚本
networks:
- blog-network
# 解决外部无法访问
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
networks:
blog-network:
driver: bridge
my.cnf
文件配置,这个也是需要配置的,否则也是会影响数据库连接的
[mysqld]
user=mysql
character-set-server=utf8mb4
default_authentication_plugin=mysql_native_password
secure_file_priv=NULL
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000
skip-name-resolve
# skip-grant-tables
log-error=/var/log/mysql/error.log
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/run/mysqld/mysqld.sock
datadir=/var/lib/mysql
tmpdir=/var/tmp
log-bin=/var/log/mysql/mysql-bin
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
- 执行
docker-compose up -d
- 如遇到上图连接数据库问题,可以能是mysql账号权限问题,配置一下即可
# 进入mysql容器命令
docker exec -it my_sql bin/bash
# 登录mysql
mysql -uroot -p123456
# 查询数据库后进入mysql查询数据表
show databases;
# 使用mysql数据库
use mysql;
# 查看数据库表
show tables;
# 查看user表中的数据
select User,Host from user;
# 查看创建的用户表没有我们设置连接的用户和host,所以需要创建
CREATE USER 'vhen'@'%' IDENTIFIED BY '123456';
# 给创建的用户赋予权限
GRANT ALL ON *.* TO 'vhen'@'%';
# 刷新权限
flush privileges;
# 退出mysql
exit;
# 如果还报错修改下密码即可
ALTER USER 'vhen'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
flush privileges;
# mysql 报错## You must reset your password using ALTER USER statement before executing this statement.
alter user user() identified by "123456";
flush privileges;
#设置密码
#PASSWORD EXPIRE NEVER 密码永不过期
#mysql_native_password 加密插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456' PASSWORD EXPIRE NEVER;
- 本地用Navicat工具连接3307端口数据库也是成功滴
- 测试一下接口
Redis
Redis是一款流行的开源(BSD许可)的内存数据存储系统,它支持多种数据类型,包括字符串、哈希表、列表、集合和有序集合。它被广泛应用于缓存、消息传递、实时分析、存储会话状态等领域。
使用场景
-
计数器
- 可以对 String 进行自增自减运算,从而实现计数器功能; 比如:计算访问次数、点赞、转发、库存数量等等
- Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量
-
缓存
- 将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率
-
会话缓存
- 可以使用 Redis 来统一存储多台应用服务器的会话信息
- 当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性
-
全页缓存(FPC
- 除基本的会话token之外,Redis还提供很简便的FPC平台
- 以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面
-
查找表
- 例如 DNS 记录就很适合使用 Redis 进行存储
- 查找表和缓存类似,也是利用了 Redis 快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为缓存不作为可靠的数据来源
-
消息队列(发布/订阅功能)
- List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息
- 不过最好使用 Kafka、RabbitMQ 等消息中间件
-
分布式锁实现
- 在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步
- 可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现
-
排名/排行榜
- 排名/排行榜(Rank/LeaderBoard)是 Redis中一个比较实用的功能,在文章热榜、游戏竞技或社区平台中,排行榜(Leaderboard)和排名(Ranking)系统是常见的功能,用于展示用户在特定活动、比赛或指标上的排名情况,而 Redis的有序集合(Sorted Set)是实现排行榜功能的理想数据结构,因为它可以存储每个成员的分数,并根据分数进行排序
-
地理位置应用
- Redis 还具有地理位置服务的特性,可以记录每个用户的位置信息,并将其保存在 Redis 中。然后通过 GeoHash 和 Geospatial 数据类型来实现位置信息的查找和计算,以实现类似于附近的人、打车等服务。
windows配置redis
RESP下载免费的Redis图形化管理软件
如果官网打不开可点击下载resp-2022.5.0.0版本
Nest集成Redis
npm install ioredis @liaoliaots/nestjs-redis
创建redis模块
nest g res redis
- redis.module.ts
import { Module, Global } from '@nestjs/common'
import { RedisService } from './redis.service'
@Global()
@Module({
providers: [RedisService],
exports: [RedisService]
})
export class RedisModule { }
- redis.service.ts
/*
* @Author: vhen
* @Date: 2024-01-02 15:11:35
* @LastEditTime: 2024-01-02 18:59:59
* @Description: 现在的努力是为了小时候吹过的牛逼!
* @FilePath: \nest-vhen-blog\src\common\libs\redis\redis.service.ts
*
*/
import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config';
import Redis from 'ioredis'
@Injectable()
export class RedisService {
private readonly redis: Redis
constructor(private readonly configService: ConfigService) {
this.redis = new Redis({
host: this.configService.get('redis.host'), // Redis 服务器的主机名
port: this.configService.get('redis.port'), // Redis 服务器的端口
password: this.configService.get('redis.password'), // Redis 服务器的密码
db: this.configService.get('redis.db'), // Redis 服务器的数据库
});
}
getClient(): Redis {
return this.redis
}
/**
* 设置值
* @param key 存储的key
* @param value 对应的值
* @param time 可选的过期时间,单位秒
* @returns
*/
async set(key: string, value: any, time: number = 0): Promise<any> {
if (time) {
return await this.redis.set(key, value, 'EX', time)
} else {
return await this.redis.set(key, value)
}
}
/**
* 获取值
* @param key 存储的key
* @returns
*/
async get(key: string): Promise<string> {
return await this.redis.get(key)
}
/**
* 删除值
* @param key 存储的key
* @returns
*/
async del(key: string): Promise<number | null> {
return await this.redis.del(key)
}
}
- app.module.ts 注册
import { RedisModule } from '@/common/libs/redis/redis.module';
@Module({
imports: [RedisModule]
})
export class AppModule { }
- demo.service.ts 测试
/*
* @Author: vhen
* @Date: 2023-12-23 19:22:25
* @LastEditTime: 2024-01-02 18:52:16
* @Description: 现在的努力是为了小时候吹过的牛逼!
* @FilePath: \nest-vhen-blog\src\modules\demo\demo.service.ts
*
*/
import { Injectable, Inject } from '@nestjs/common';
import { RedisService } from '@/common/libs/redis/redis.service';
@Injectable()
export class DemoService {
constructor(@Inject(RedisService) private readonly redisService: RedisService) {}
async findAll() {
this.redisService.set('nest_redis_test', '测试redis')
console.log('nest_redis_test', await this.redisService.get('nest_redis_test'));
return `This action returns all demo`;
}
}
github
项目地址:nest_vhen_blog