Nginx的镜像制作

1·基于centos:7编译安装nginx

Dockerfile

  1. FROM docker.io/centos:centos7.6.1810
  2. ENV NGINX_VERSION 1.14.2
  3. ENV LANG "en_US.UTF-8"
  4. RUN set -x && \
  5. yum install -y epel-release \
  6. make \
  7. gcc \
  8. gcc-c++ \
  9. pcre-devel \
  10. zlib-devel \
  11. openssl-devel \
  12. gd-devel \
  13. vim \
  14. net-tools \
  15. telnet \
  16. curl && \
  17. # Add user
  18. mkdir -p /data/www && \
  19. useradd -M -s /sbin/nologin www && \
  20. # Download nginx
  21. mkdir -p /home/nginx && \
  22. cd $_ && \
  23. curl -Lk http://downloads.ichzh.com/chzh/nginx/nginx-$NGINX_VERSION.tar.gz | gunzip | tar x -C /home/nginx && \
  24. # Make install nginx
  25. cd /home/nginx/nginx-$NGINX_VERSION && \
  26. ./configure --prefix=/usr/local/nginx \
  27. --sbin-path=/usr/local/nginx/sbin/nginx \
  28. --error-log-path=/var/log/nginx_error.log \
  29. --http-log-path=/var/log/nginx_access.log \
  30. --pid-path=/usr/local/nginx/run/nginx.pid \
  31. --lock-path=/usr/local/nginx/run/nginx.lock \
  32. --user=www \
  33. --group=www \
  34. --with-compat \
  35. --with-file-aio \
  36. --with-threads \
  37. --with-http_addition_module \
  38. --with-http_auth_request_module \
  39. --with-http_dav_module \
  40. --with-http_flv_module \
  41. --with-http_gunzip_module \
  42. --with-http_gzip_static_module \
  43. --with-http_mp4_module \
  44. --with-http_random_index_module \
  45. --with-http_realip_module \
  46. --with-http_secure_link_module \
  47. --with-http_slice_module \
  48. --with-http_ssl_module \
  49. --with-http_stub_status_module \
  50. --with-http_sub_module \
  51. --with-http_v2_module \
  52. --with-mail \
  53. --with-mail_ssl_module \
  54. --with-stream \
  55. --with-stream_realip_module \
  56. --with-stream_ssl_module \
  57. --with-stream_ssl_preread_module && \
  58. make && make install && \
  59. #Clean OS
  60. yum remove -y gcc \
  61. gcc-c++ \
  62. make && \
  63. yum clean all && \
  64. rm -rf /tmp/* /var/cache/yum/* && \
  65. find /var/log -type f -delete && \
  66. rm -rf /home/nginx && \
  67. rm -rf /usr/local/nginx/html/* && \
  68. # Change own for webdir
  69. chown -R www:www /data/www && \
  70. # Set timezone
  71. ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
  72. # Set log
  73. ln -sf /dev/stdout /var/log/nginx_access.log && \
  74. ln -sf /dev/stderr /var/log/nginx_error.log
  75. # Add nginx.conf
  76. ADD nginx.conf /usr/local/nginx/conf/
  77. #ADD index.html /data/www/
  78. ENV PATH $PATH:/usr/local/nginx/sbin
  79. #USER www
  80. WORKDIR /usr/local/nginx
  81. EXPOSE 80
  82. CMD ["nginx", "-g", "daemon off;"]

centos-nginx.gif

2·官方nginx镜像构建

#This is DockerFilr based on the NGINX image

FROM nginx:1.20.1-alpine

LABEL Dreambeer="Dreambeer@126.com"
LABEL version="nginx:1.20.1"

COPY index.html /usr/share/nginx/html/index.html

#docker run -d -it --name nginx-jnginx -p 81:80 --rm nginx:1
#docker build -t nginx:1 ./

本地index.html文件

<h1>Dreambeer nginx web </h1>

3·基于alpine构建nginx最小镜像

#This is DockerFile based on the alpine image

FROM alpine:3.14.0
LABEL MAINTAINER="Dreambeer@126.com"
ENV PATH $PATH:/usr/local/nginx/sbin/
ENV LANG "en_US.UTF-8"
ENV NGINX_VERSION 1.14.2
ARG CPU_NUM
# 修改源
RUN echo "http://mirrors.aliyun.com/alpine/latest-stable/main/" > /etc/apk/repositories && \
    echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >> /etc/apk/repositories && \
    apk --no-cache update && \
    apk add --no-cache tzdata bash && \
    apk add --no-cache gcc libc-dev zlib-dev pcre-dev make openssl-dev net-tools wget vim && \
# 创建用户创建用户组
    addgroup -S www && \
    adduser  -s /sbin/nologin -S -D -G www www && \
# 创建用户目录赋权绑定
    mkdir -p /data/www && \
    chown -R www:www /data/www && \
# 编译安装
    wget -P /usr/local/ http://downloads.ichzh.com/Nginx/nginx-$NGINX_VERSION.tar.gz && \
    cd /usr/local/ && \
    tar -zxvf nginx-$NGINX_VERSION.tar.gz && \
    cd /usr/local/nginx-$NGINX_VERSION && \
    ./configure --user=www \ 
    --group=www \
    --prefix=/usr/local/nginx \
    --with-http_stub_status_module \
    --without-http-cache \
    --with-http_ssl_module \
    --sbin-path=/usr/local/nginx/sbin/nginx \
    --conf-path=/usr/local/nginx/conf/nginx.conf \
    --error-log-path=/var/log/nginx_error.log \
    --http-log-path=/var/log/nginx_access.log \
    --pid-path=/usr/local/nginx/run/nginx.pid \
    --lock-path=/usr/local/nginx/run/nginx.lock \
    --with-compat \
    --with-threads \
    --with-http_addition_module \
    --with-http_auth_request_module \
    --with-http_dav_module \
    --with-http_gzip_static_module \
    --with-http_mp4_module \
    --with-http_random_index_module \
    --with-http_realip_module \
    --with-http_secure_link_module \
    --with-http_slice_module \
    --with-http_sub_module \
    --with-http_v2_module \
    --with-mail \
    --with-mail_ssl_module \
    --with-stream \
    --with-stream_realip_module \
    --with-stream_ssl_module \
    --with-stream_ssl_preread_module && \
    make -j$CPU_NUM && make install && \
    #清理运行时不需要的软件和安装缓存
    rm -rf /var/cache/apk/* && \
    rm -rf /root/.cache && \
    rm -rf /tmp/* && \
    rm -rf /usr/local/nginx-$NGINX_VERSION && \
    rm -rf /usr/local/nginx-$NGINX_VERSION.tar.gz && \
    apk del tzdata bash && \
    apk del gcc libc-dev zlib-dev make openssl-dev wget && \
    #根据官方镜像设置日志
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    ln -sf /dev/stdout /var/log/nginx_access.log && \
    ln -sf /dev/stderr /var/log/nginx_error.log 
#添加本地配置文件
# ADD nginx.conf /usr/local/nginx/conf/
EXPOSE 80
WORKDIR /usr/local/nginx
CMD ["nginx","-g","daemon off;"]

#docker run -it -d --name nginx -v /Docker/nginx-jalpine-image/data:/usr/local/nginx/html/ -p 81:80 --rm nginx-1.20.1:alpine
#docker run -it -d --name nginx -p 81:80 --rm nginx-1.20.1:alpine
#docker build -t nginx:alpine --build-arg CPU_NU=<机器CPU核数> ./
#查看核数"cat /proc/cpuinfo | grep processor | wc -l"

alpine-nginx.gif

apk常用命令

alpine 提供了非常好用的apk软件包管理工具,通过apk –help命令查看完整的包管理命令。

更换使用源

查看本地使用源(具体更换方法下面有谈)
image.png

更新索引

update:从远程镜像源中更新本地镜像源索引,update命令会从各个镜像源列表下载APKINDEX.tar.gz并存储到本地缓存,一般在/var/cache/apk/(Alpine在该目录下)
image.png

apk update

update:从远程镜像源中更新本地镜像源索引。
update命令会从各个镜像源列表下载APKINDEX.tar.gz并存储到本地缓存,一般在/var/cache/apk/(Alpine在该目录下)、**/var/lib/apk/** **/etc/apk/cache/**下。

apk add
add:安装PACKAGES并自动解决依赖关系。
add命令从仓库中安装最新软件包,并自动安装必须的依赖包,也可以从第三方仓库添加软件包。

$ apk add openssh openntp vim
$ apk add --no-cache mysql-client
$ apk add docker --update-cache --repository http://mirrors.ustc.edu.cn/alpine/v3.4/main/ --allow-untrusted

安装指定版本软件包

$ apk add asterisk=1.6.0.21-r0
$ apk add 'asterisk<1.6.1'
$ apk add 'asterisk>1.6.1

apk del
del:卸载并删除PACKAGES

apk del openssh openntp vim

apk upgrade
upgrade:升级当前已安装的软件包。
upgrade命令升级系统已安装的所有软件包(一般包括内核),当然也可指定仅升级部分软件包(通过-u或–upgrade选择指定)。

$ apk update #更新最新本地镜像源
$ apk upgrade #升级软件
$ apk add --upgrade busybox #指定升级部分软件包

apk search
search:搜索软件包。
search命令搜索可用软件包,-v 参数输出描述内容,支出通配符,-d 或 –description 参数指定通过软件包描述查询。

$ apk search #查找所有可用软件包
$ apk search -v #查找所有可用软件包及其描述内容
$ apk search -v 'acf*' #通过软件包名称查找软件包
$ apk search -v -d 'docker' #通过描述文件查找特定的软件包

apk info
info:列出PACKAGES或镜像源的详细信息。
info命令用于显示软件包的信息。

$ apk info #列出所有已安装的软件包
$ apk info -a zlib #显示完整的软件包信息
$ apk info --who-owns /sbin/lbu #显示指定文件属于的包

1, 建立一个指定GID的组

addgroup -g 10001 -S groupA

addgroup

1 addgroup [-g GID] [-S] [USER] GROUP

addgroup一般情况下创建用户的同时会创建组,包括其ID号,在实际使用中addgroup使用较少。
-g GID:用户组GID
-S:创建系统组(GID100~999)
举个栗子
创建一个用户,用户名是whsir(默认会同时创建一个whsir的组)

adduser whsir

创建一个用户,用户名是whsir,其UID是1200(默认情况下UID和GID是一样的)

adduser whsir -u 1200

创建一个用户,用户名是whsir,不创建密码,不创建用户家目录,其UID是1200

adduser whsir -u 1200 -D -H

创建一个用户,用户名是whsir,不创建密码,不创建用户家目录,其UID是1200,指定用户shell是/sbin/nologin

adduser whsir -u 1200 -D -H -s /sbin/nologin

2, 建立一个指定UID的用户,指定shell, 让它属于指定的用户组

adduser  userA -u 20001 -D -S -s /bin/bash -G groupA

adduser

1 adduser [OPTIONS] USER [GROUP]

adduser创建新用户或将用户添加到组
-h DIR:创建用户时指定用户家目录位置,默认/home/NAME
-g GECOS:用户备注信息,即/etc/passwd第五个字段
-s SHELL:指定用户所使用的shell,默认/bin/ash
-G GRP:指定用户所属的组
-S:创建系统用户(UID号100~999),创建系统用户时不自动创建组,默认情况下创建用户时会同时创建一个与账号同名的组
-D:创建用户时不创建密码
-H:创建用户时不创建用户家目录
-u UID:指定用户UID
-k SKEL:指定骨骼框架目录位置,默认/etc/skel,其实就是用来放置新用户配置文件的,添加一个新用户时,会将该框架目录中的文件复制到新用户的家目录下。
例:创建骨骼框架目录为/etc/skel/,在/etc/skel/目录下touch一个123.txt,创建新用户whsir并指定/etc/skel/,创建后可发现,在whsir用户的家目录下存在123.txt文件了,这个文件就是我们刚才touch的文件。

1
2
3
4
mkdir /etc/skel/
touch /etc/skel/123.txt
adduser whsir -k /etc/skel/
ls /home/whsir/123.txt


3, 让用户可以使用su -到root用户下**

chmod 4755 /bin/busybox

4,root更改密码。(这条和上条,可以在dockerfile里提前实现)

echo -e "rootpwd\nrootpwd" | passwd root

5,dockerfile里,可以同时改变用户同时COPY文件到镜像里。(为什么一定要同时作?如果不同时作,docker的layer增加一层,改权限和属主,镜像大小会翻倍。)
你试过仅仅修改一个JDK的权限和属主,镜像增加近180M的事儿么?但这个高版本的DOCKER才支持这样的语法。

COPY --chown=userA:groupA bs.sh ${BS_DIR}

alpine中apk安装源的处理

包管理工具apk
  • apk add 包名 #安装 xx包
  • apk del 包名 #卸载 xx包
  • apk update 更新源

    永久修改apk下载源地址

    使用阿里云

  • 编辑 /etc/apk/repositories

  • 将里面 dl-cdn.alpinelinux.org 的 改成mirrors.aliyun.com 保存退出即可
    临时更改apk下载源地址
    直接在软件安装后面 添加源地址
    apk add php5.6 --repository http://nl.alpinelinux.org/alpine/edge/testing/
    
    即可使用当前源进行安装。
    找apk 的包名
    在阿里云的镜像地址中搜索相关的包名即可
    http://mirrors.aliyun.com/alpine/v3.11/main
    http://mirrors.aliyun.com/alpine/v3.11/community
    
    修改源
    RUN echo "http://mirrors.aliyun.com/alpine/latest-stable/main/" > /etc/apk/repositories && \
         echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >> /etc/apk/repositories
    
    安装时区软件, 如果有自定义软件, 继续在后面添加
    apk add --no-cache tzdata
    
    设置默认时区
    cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
    

    容器优化之(alpine中apk缓存依赖优化)

    注意优化很重要,亲测原166MB的镜像优化完16.3MB
    清除apk缓存
    rm -rf /var/cache/apk/* && \
    rm -rf /root/.cache && \
    rm -rf /tmp/* && \
    rm -rf /usr/local/nginx-1.20.1 && \
    卸载依赖安装包
    apk del --no-cache tzdata bash && \
    apk del gcc libc-dev zlib-dev make openssl-dev wget
    

    运行Docker容器时附加PATH环境变量

    ``` ENV PATH=/usr/local/nginx/sbin/:$PATH 将本实例中nginx中的启动运行加到$PATH环境变量中去

参考:https://www.thinbug.com/q/37281533

<a name="4Xv6z"></a>
## linux中添加变量
1.设置值<br />`$name=test`<br />2. 输出变量的值 echo<br />`$echo $name`<br />3. 增加变量内容<br />`PATH=$PATH:/home/bin/test`<br />`PATH="$PATH":/home/bin/test`<br />`PATH=${PATH}:/home/bin/test`<br />4.若该变量需要在其他子进程执行,则需要以export来使变量变成环境变量;<br />`export PATH`<br />`$name=test`<br />`$bash`<br />`$echo $name`<br />`$exit`<br />`$export name`<br />`$bash`<br />`$echo $name`<br />`$exit`
<a name="w5TsF"></a>
## 日志定向标准输出
Docker 日志分为两类:

- Docker 引擎日志(也就是 dockerd 运行时的日志),
- 容器的日志,容器内的服务产生的日志。
<a name="TwVjv"></a>
### Docker 引擎日志
Docker 引擎日志一般是交给了 Upstart(Ubuntu 14.04) 或者 systemd (CentOS 7, Ubuntu 16.04)。前者一般位于 /var/log/upstart/docker.log 下,后者我们一般 通过 journalctl -u docker 来进行查看。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624172819354-3a3dff09-7f58-434a-874a-fd7a4203ce42.png#align=left&display=inline&height=405&margin=%5Bobject%20Object%5D&name=image.png&originHeight=405&originWidth=966&size=34364&status=done&style=none&width=966)
<a name="i0oth"></a>
### 容器日志
<a name="tuBSH"></a>
#### 常用查看日志命令——docker logs
docker logs CONTAINER 显示当前运行的容器的日志信息, UNIX 和 Linux 的命令有三种 输入输出,分别是 STDIN(标准输入)、STDOUT(标准输出)、STDERR(标准错误输出),docker logs 显示的内容包含 STOUT 和 STDERR。在生产环境,如果我们的应用输出到我们的日志文件里,所以我们在使用 docker logs 一般收集不到太多重要的日志信息。
> - nginx 官方镜像,使用了一种方式,让日志输出到 STDOUT,也就是 创建一个符号链接 /var/log/nginx/access.log 到 /dev/stdout。
> - httpd 使用的是 让其输出到指定文件 ,正常日志输出到 /proc/self/fd/1 (STDOUT) ,错误日志输出到 /proc/self/fd/2 (STDERR)。
> - 当日志量比较大的时候,我们使用 docker logs 来查看日志,会对 docker daemon 造成比较大的压力,容器导致容器创建慢等一系列问题。
> - 只有使用了 local 、json-file、journald 的日志驱动的容器才可以使用 docker logs 捕获日志,使用其他日志驱动无法使用 docker logs

<a name="sIR9N"></a>
#### Docker 日志 驱动
Docker 提供了两种模式用于将消息从容器到日志驱动。

- (默认)拒绝,阻塞从容器到容器驱动
- 非阻塞传递,日志将储存在容器的缓冲区。
> 当缓冲区满,旧的日志将被丢弃。

在 mode 日志选项控制使用 `blocking(默认)` 或者 `non-blocking`, 当设置为 `non-blocking` 需要设置 `max-buffer-size` 参数(默认为 1MB)。<br />**支持的驱动**<br />**![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624173031075-f958e8e4-6a1d-47f2-ae6b-1af669904ec6.png#align=left&display=inline&height=523&margin=%5Bobject%20Object%5D&name=image.png&originHeight=523&originWidth=979&size=55762&status=done&style=none&width=979)**<br />使用 Docker-CE 版本,`docker logs`命令 仅仅适用于以下驱动程序(前面 docker logs 详解也提及到了)

- local
- json-file
- journald

![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624173065290-42a0b9d8-f07c-42d3-a8a9-c78d9ecb162f.png#align=left&display=inline&height=499&margin=%5Bobject%20Object%5D&name=image.png&originHeight=499&originWidth=906&size=23520&status=done&style=none&width=906)
<a name="T5VVp"></a>
#### Docker 日志驱动常用命令
查看系统当前设置的日志驱动

docker info |grep “Logging Driver” / docker info —format ‘{{.LoggingDriver}}’

查看单个容器的设置的日志驱动

docker inspect -f ‘{{.HostConfig.LogConfig.Type}}’ 容器id

<a name="QNFDl"></a>
#### Docker 日志驱动全局配置更改
修改日志驱动,在配置文件 `/etc/docker/daemon.json`(注意该文件内容是 JSON 格式的)进行配置即可。<br />示例:

{ “log-driver”: “syslog” }

以上更改是针对所有的容器的日志驱动的。我们也可以单独为单一容器设置日志驱动。
<a name="DwRf8"></a>
#### Docker 单一容器日志驱动配置
在 运行容器的时候指定 日志驱动 `--log-driver`。

docker run -itd —log-driver none alpine ash # 这里指定的日志驱动为 none

<a name="uEfiQ"></a>
#### 日志驱动 一 、local

1. local 日志驱动 记录从容器的 STOUT/STDERR 的输出,并写到宿主机的磁盘。
1. 默认情况下,local 日志驱动为每个容器保留 100MB 的日志信息,并启用自动压缩来保存。(经过测试,保留100MB 的日志是指没有经过压缩的日志)
1. local 日志驱动的储存位置 /var/lib/docker/containers/容器id/local-logs/ 以 container.log 命名。

**local 驱动支持的选项**<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624173242111-962f1b0b-3ce3-4214-92a1-1d63d693ec38.png#align=left&display=inline&height=214&margin=%5Bobject%20Object%5D&name=image.png&originHeight=214&originWidth=976&size=24018&status=done&style=none&width=976)<br />**全局日志驱动设置为—local**<br />在配置文件 /etc/docker/daemon.json(注意该文件内容是 JSON 格式的)进行配置即可。

{ “log-driver”: “local”, “log-opts”: { “max-size”: “10m” } }

重启 docker 即可生效。

**单个容器日志驱动设置为—local**<br />运行容器并设定为 local 驱动。

#  运行一个容器 ,并设定日志驱动为 local ,并运行命令 ping www.baidu.com

[root@localhost docker]# docker run -itd —log-driver local alpine ping www.baidu.com 3795b6483534961c1d5223359ad1106433ce2bf25e18b981a47a2d79ad7a3156

#  查看运行的容器的 日志驱动是否是 local

[root@localhost docker]# docker inspect -f ‘{{.HostConfig.LogConfig.Type}}’ 3795b6483534961c local

# 查看日志

[root@localhost local-logs]# tail -f /var/lib/docker/containers/3795b6483534961c1d5223359ad1106433ce2bf25e18b981a47a2d79ad7a3156/local-logs/container.log NNdoutםѰ͕̈:64 bytes from 14.215.177.38: seq=816 ttl=55 time=5.320 ms NNdoutهµ͕̈͡:64 bytes from 14.215.177.38: seq=817 ttl=55 time=4.950 ms

注意事项: 经过测试,当我们产生了100 MB 大小的日志时 会有 四个压缩文件和一个 container.log:

[root@localhost local-logs]# ls -l total 32544 -rw-r——-. 1 root root 18339944 May 16 09:41 container.log -rw-r——-. 1 root root 3698660 May 16 09:41 container.log.1.gz -rw-r——-. 1 root root 3726315 May 16 09:41 container.log.2.gz -rw-r——-. 1 root root 3805668 May 16 09:41 container.log.3.gz -rw-r——-. 1 root root 3744104 May 16 09:41 container.log.4.gz

那么当超过了 100MB 的日志文件,日志文件会继续写入到 container.log,但是会将 container.log 日志中老的日志删除,追加新的,也就是 当写满 100MB 日志后 ,再产生一条新日志,会删除 container.log 中的一条老日志,保存 100MB 的大小。这个 对我们是会有一些影响的,
> 当我运行系统时 第一天由于bug产生了 100MB 日志,那么之前的日志就已经有 80MB 日志变成的压缩包,所以我在后续的运行中,只能获取最近的 20MB日志。

<a name="wKspI"></a>
#### 
<a name="1pVPt"></a>
#### 日志驱动 二、 默认的日志驱动—JSON
所有容器默认的日志驱动 json-file。<br />json-file 日志驱动 记录从容器的 STOUT/STDERR 的输出 ,用 JSON 的格式写到文件中,日志中不仅包含着 输出日志,还有时间戳和 输出格式。下面是一个 ping www.baidu.com 对应的 JSON 日志

{“log”:”64 bytes from 14.215.177.39: seq=34 ttl=55 time=7.067 ms\r\n”,”stream”:”stdout”,”time”:”2019-05-16T14:14:15.030612567Z”}

json-file 日志的路径位于` /var/lib/docker/containers/container_id/container_id-json.log`。<br />json-file 的 日志驱动支持以下选项:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624173509626-acf711c9-e7b2-4773-97e3-3e1f91ba6960.png#align=left&display=inline&height=373&margin=%5Bobject%20Object%5D&name=image.png&originHeight=373&originWidth=978&size=49526&status=done&style=none&width=978)<br />**json-file 的日志驱动示例**<br /># 设置 日志驱动为 json-file ,我们也可以不设置,因为默认就是 json-file

docker run -itd —name test-log-json —log-driver json-file alpine ping www.baidu.com 199608b2e2c52136d2a17e539e9ef7fbacf97f1293678aded421dadbdb006a5e

# 查看日志,日志名称就是 容器名称-json.log

tail -f /var/lib/docker/containers/199608b2e2c52136d2a17e539e9ef7fbacf97f1293678aded421dadbdb006a5e/199608b2e2c52136d2a17e539e9ef7fbacf97f1293678aded421dadbdb006a5e-json.log {“log”:”64 bytes from 14.215.177.39: seq=13 ttl=55 time=15.023 ms\r\n”,”stream”:”stdout”,”time”:”2019-05-16T14:13:54.003118877Z”} {“log”:”64 bytes from 14.215.177.39: seq=14 ttl=55 time=9.640 ms\r\n”,”stream”:”stdout”,”time”:”2019-05-16T14:13:54.999011017Z”} {“log”:”64 bytes from 14.215.177.39: seq=15 ttl=55 time=8.938 ms\r\n”,”stream”:”stdout”,”time”:”2019-05-16T14:13:55.998612636Z”} {“log”:”64 bytes from 14.215.177.39: seq=16 ttl=55 time=18.086 ms\r\n”,”stream”:”stdout”,”time”:”2019-05-16T14:13:57.011235913Z”} {“log”:”64 bytes from 14.215.177.39: seq=17 ttl=55 time=12.615 ms\r\n”,”stream”:”stdout”,”time”:”2019-05-16T14:13:58.007104112Z”} {“log”:”64 bytes from 14.215.177.39: seq=18 ttl=55 time=11.001 ms\r\n”,”stream”:”stdout”,”time”:”2019-05-16T14:13:59.007559413Z”}


<a name="eYwYa"></a>
#### 日志驱动 三、syslog
syslog 日志驱动将日志路由到 syslog 服务器,syslog 以原始的字符串作为 日志消息元数据,接收方可以提取以下的消息:

- level 日志等级 ,如debug,warning,error,info。
- timestamp 时间戳
- hostname 事件发生的主机
- facillty 系统模块
- 进程名称和进程 ID

**syslog 日志驱动全局配置**<br />编辑 `/etc/docker/daemon.json` 文件

{ “log-driver”: “syslog”, “log-opts”: { “syslog-address”: “udp://1.2.3.4:1111” } }

重启 docker 即可生效。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624173671929-63ed843a-fd10-4426-bfce-73ec71d9c10c.png#align=left&display=inline&height=740&margin=%5Bobject%20Object%5D&name=image.png&originHeight=740&originWidth=983&size=103281&status=done&style=none&width=983)<br />****单个容器日志驱动设置为—syslog ****<br />Linux 系统中 我们用的系统日志模块时 rsyslog ,它是基于syslog 的标准实现。我们要使用 syslog 驱动需要使用 系统自带的 rsyslog 服务。<br /># 查看当前 rsyslog 版本和基本信息

[root@localhost harbor]# rsyslogd -v rsyslogd 8.24.0, compiled with: PLATFORM: x86_64-redhat-linux-gnu PLATFORM (lsb_release -d):
FEATURE_REGEXP: Yes GSSAPI Kerberos 5 support: Yes FEATURE_DEBUG (debug build, slow code): No 32bit Atomic operations supported: Yes 64bit Atomic operations supported: Yes memory allocator: system default Runtime Instrumentation (slow code): No uuid support: Yes Number of Bits in RainerScript integers: 64

See [`http://www.rsyslog.com`](http://www.rsyslog.com) for more information.<br />配置 syslog , 在配置文件 /etc/rsyslog.conf 大约14-20行,我们可以看到两个配置,一个udp,一个tcp ,都是监听 514 端口,提供 syslog 的接收。选择 tcp 就将 tcp 的两个配置的前面 # 号注释即可。

Provides UDP syslog reception

$ModLoad imudp

$UDPServerRun 514

Provides TCP syslog reception

$ModLoad imtcp

$InputTCPServerRun 514

然后重启 rsyslog,我们可以看到514端口在监听。

systemctl restart rsyslog [root@localhost harbor]# netstat -ntul |grep 514 tcp 0 0 0.0.0.0:514 0.0.0.0: LISTEN
tcp6 0 0 :::514 :::
LISTEN

启动一个以 syslog 为驱动的容器。

docker run -d -it -p 87:80 —log-driver syslog —log-opt syslog-address=tcp://127.0.0.1:514 —name nginx-syslog nginx

访问并查看日志<br /># 访问nginx<br />`curl 127.0.0.1:87`<br /># 查看访问日志

tail -f /var/log/messages May 17 15:56:48 localhost fe18924aefde[6141]: 172.17.0.1 - - [17/May/2019:07:56:48 +0000] “GET / HTTP/1.1” 200 612 “-“ “curl/7.29.0” “-“#015 May 17 15:58:16 localhost fe18924aefde[6141]: 172.17.0.1 - - [17/May/2019:07:58:16 +0000] “GET / HTTP/1.1” 200 612 “-“ “curl/7.29.0” “-“#015

<a name="RvEzU"></a>
#### 日志驱动 四、Journald
journald 日志驱动程序将容器的日志发送到 systemd journal, 可以使用 journal API 或者使用 docker logs 来查日志。<br />除了日志本身以外, journald 日志驱动还会在日志加上下面的数据与消息一起储存。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624174250842-669c9f46-0dea-4ebb-a15d-76e562ac1479.png#align=left&display=inline&height=247&margin=%5Bobject%20Object%5D&name=image.png&originHeight=247&originWidth=971&size=23522&status=done&style=none&width=971)<br />选项<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624174270683-79e95923-1216-40d2-8a04-4ef9ebefd273.png#align=left&display=inline&height=206&margin=%5Bobject%20Object%5D&name=image.png&originHeight=206&originWidth=971&size=25781&status=done&style=none&width=971)<br />**journald 日志驱动全局配置**<br />编辑 /etc/docker/daemon.json 文件

{ “log-driver”: “journald” }

单个容器日志驱动设置为—journald

docker run -d -it —log-driver=journald \ —log-opt labels=location \ —log-opt env=TEST \ —env “TEST=false” \ —label location=china \ —name nginx-journald\ -p 80:80\ nginx

查看日志 journalctl

只查询指定容器的相关消息

journalctl CONTAINER_NAME=webserver

-b 指定从上次启动以来的所有消息

journalctl -b CONTAINER_NAME=webserver

-o 指定日志消息格式,-o json 表示以json 格式返回日志消息

journalctl -o json CONTAINER_NAME=webserver

-f 一直捕获日志输出

journalctl -f CONTAINER_NAME=webserver

> 如果我们的容器在启动的时候加了 -t 参数,启用了 TTY 的话,那么我查看日志是会像下面一样
> `May 17 17:19:26 localhost.localdomain 2a338e4631fe[6141]: [104B blob data]`
> `May 17 17:19:32 localhost.localdomain 2a338e4631fe[6141]: [104B blob data]`
> 显示[104B blob data] 而不是完整日志原因是因为有 \r 的存在,如果我们要完整显示,需要加上参数 --all 。

<a name="DhQoM"></a>
#### 生产环境中该如何储存容器中的日志
我们在上面看到了 Docker 官方提供了 很多日志驱动,但是上面的这些驱动都是针对的 标准输出的日志驱动。<br />**容器日志分类**<br />容器的日志实际是有两大类的:
> 标准输出的 ,也就是 STDOUT 、STDERR ,这类日志我们可以通过 Docker 官方的日志驱动进行收集。

示例:Nginx 日志,Nginx 日志有 access.log 和 error.log ,我们在 Docker Hub 上可以看到 Nginx 的 dockerfile 对于这两个日志的处理是:

RUN ln -sf /dev/stdout /var/log/nginx/access.log \ && ln -sf /dev/stderr /var/log/nginx/error.log

都软连接到 /dev/stdout 和 /dev/stderr ,也就是标准输出,所以这类 容器是可以使用 Docker 官方的日志驱动。<br />**文本日志**,存在在于容器内部,并没有重定向到 容器的标准输出的日志。<br />示例: Tomcat 日志,Tomcat 有 catalina、localhost、manager、admin、host-manager,我们可以在 Docker Hub 看到 Tomcat 的 dockerfile 只有对于 catalina 进行处理,其它日志将储存在容器里。

CMD [“catalina.sh”, “run”]

我们运行了一个 Tomcat 容器 ,然后进行访问后,并登陆到容器内部,我们可以看到产生了文本日志:

root@25ba00fdab97:/usr/local/tomcat/logs# ls -l total 16 -rw-r——-. 1 root root 6822 May 17 14:36 catalina.2019-05-17.log -rw-r——-. 1 root root 0 May 17 14:36 host-manager.2019-05-17.log -rw-r——-. 1 root root 459 May 17 14:36 localhost.2019-05-17.log -rw-r——-. 1 root root 1017 May 17 14:37 localhost_access_log.2019-05-17.txt -rw-r——-. 1 root root 0 May 17 14:36 manager.2019-05-17.log

我们下面有专门的方案来应对。<br />**一、当是完全是标准输出的类型的容器**<br />我们可以选择 json-file 、syslog、local 等 Docker 支持的日志驱动。<br />**二、当有文件文本日志的类型容器**<br />**方案一 挂载目录 bind**<br />创建一个目录,将目录挂载到 容器中产生日志的目录。

—mount type=bind,src=/opt/logs/,dst=/usr/local/tomcat/logs/

示例:

创建挂载目录/opt/logs

[root@fy-local-2 /]# mkdir /opt/logs

创建容器tomcat-bind 并将 /opt/logs 挂载至 /usr/local/tomcat/logs/

[root@fy-local-2 /]# docker run -d —name tomcat-bind -P —mount type=bind,src=/opt/logs/,dst=/usr/local/tomcat/logs/ tomcat [root@fy-local-2 /]# ls -l /opt/logs/ total 12 -rw-r——- 1 root root 6820 May 22 17:31 catalina.2019-05-22.log -rw-r——- 1 root root 0 May 22 17:31 host-manager.2019-05-22.log -rw-r——- 1 root root 459 May 22 17:31 localhost.2019-05-22.log -rw-r——- 1 root root 0 May 22 17:31 localhost_access_log.2019-05-22.txt -rw-r——- 1 root root 0 May 22 17:31 manager.2019-05-22.log

**方案二 使用数据卷 volume**<br />创建数据卷,创建容器时绑定数据卷,

—mount type=volume src=volume_name dst=/usr/local/tomcat/logs/

示例:

创建tomcat应用数据卷名称为 tomcat

[root@fy-local-2 /]# docker volume create tomcat

创建容器tomcat-volume 并指定数据卷为 tomcat,绑定至 /usr/local/tomcat/logs/

[root@fy-local-2 /]# docker run -d —name tomcat-volume -P —mount type=volume,src=tomcat,dst=/usr/local/tomcat/logs/ tomcat

查看数据卷里面的内容

[root@fy-local-2 /]# ls -l /var/lib/docker/volumes/tomcat/_data/ total 12 -rw-r——- 1 root root 6820 May 22 17:33 catalina.2019-05-22.log -rw-r——- 1 root root 0 May 22 17:33 host-manager.2019-05-22.log -rw-r——- 1 root root 459 May 22 17:33 localhost.2019-05-22.log -rw-r——- 1 root root 0 May 22 17:33 localhost_access_log.2019-05-22.txt -rw-r——- 1 root root 0 May 22 17:33 manager.2019-05-22.log

**方案三 计算容器 rootfs 挂载点**<br />使用挂载宿主机目录的方式采集日志对应用会有一定的侵入性,因为它要求容器启动的时候包含挂载命令。如果采集过程能对用户透明那就太棒了。事实上,可以通过计算容器 rootfs 挂载点来达到这种目的。<br />和容器 rootfs 挂载点密不可分的一个概念是 storage driver。实际使用过程中,用户往往会根据 linux 版本、文件系统类型、容器读写情况等因素选择合适的 storage driver。不同 storage driver 下,容器的 rootfs 挂载点遵循一定规律,因此我们可以根据 storage driver 的类型推断出容器的 rootfs 挂载点,进而采集容器内部日志。下表展示了部分 storage dirver 的 rootfs 挂载点及其计算方法。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624177373397-94ab171b-d39c-45c8-9294-e4965cb3f8e1.png#align=left&display=inline&height=321&margin=%5Bobject%20Object%5D&name=image.png&originHeight=321&originWidth=973&size=39154&status=done&style=none&width=973)<br />示例:

创建容器 tomcat-test

[root@fy-local-2 /]# docker run -d —name tomcat-test -P tomcat 36510dd653ae7dcac1d017174b1c38b3f9a226f9c4e329d0ff656cfe041939ff

查看tomcat-test 容器的 挂载点位置

[root@fy-local-2 /]# docker inspect -f ‘{{.GraphDriver.Data.MergedDir}}’ 36510dd653ae7dcac1d017174b1c38b3f9a226f9c4e329d0ff656cfe041939ff
/var/lib/docker/overlay2/c10ec54bab8f3fccd2c5f1a305df6f3b1e53068776363ab0c104d253216b799d/merged

查看挂载点的目录结构

[root@fy-local-2 /]# ls -l /var/lib/docker/overlay2/c10ec54bab8f3fccd2c5f1a305df6f3b1e53068776363ab0c104d253216b799d/merged total 4 drwxr-xr-x 1 root root 179 May 8 13:05 bin drwxr-xr-x 2 root root 6 Mar 28 17:12 boot drwxr-xr-x 1 root root 43 May 22 17:27 dev lrwxrwxrwx 1 root root 33 May 8 13:08 docker-java-home -> /usr/lib/jvm/java-8-openjdk-amd64 drwxr-xr-x 1 root root 66 May 22 17:27 etc drwxr-xr-x 2 root root 6 Mar 28 17:12 home drwxr-xr-x 1 root root 6 May 16 08:50 lib drwxr-xr-x 2 root root 34 May 6 08:00 lib64 drwxr-xr-x 2 root root 6 May 6 08:00 media drwxr-xr-x 2 root root 6 May 6 08:00 mnt drwxr-xr-x 2 root root 6 May 6 08:00 opt drwxr-xr-x 2 root root 6 Mar 28 17:12 proc drwx——— 1 root root 27 May 22 17:29 root drwxr-xr-x 3 root root 30 May 6 08:00 run drwxr-xr-x 2 root root 4096 May 6 08:00 sbin drwxr-xr-x 2 root root 6 May 6 08:00 srv drwxr-xr-x 2 root root 6 Mar 28 17:12 sys drwxrwxrwt 1 root root 29 May 16 08:50 tmp drwxr-xr-x 1 root root 19 May 6 08:00 usr drwxr-xr-x 1 root root 41 May 6 08:00 var

查看日志

[root@fy-local-2 /]# ls -l /var/lib/docker/overlay2/c10ec54bab8f3fccd2c5f1a305df6f3b1e53068776363ab0c104d253216b799d/merged/usr/local/tomcat/logs/ total 20 -rw-r——- 1 root root 14514 May 22 17:40 catalina.2019-05-22.log -rw-r——- 1 root root 0 May 22 17:27 host-manager.2019-05-22.log -rw-r——- 1 root root 1194 May 22 17:40 localhost.2019-05-22.log -rw-r——- 1 root root 0 May 22 17:27 localhost_access_log.2019-05-22.txt -rw-r——- 1 root root 0 May 22 17:27 manager.2019-05-22.log

**方案四 在代码层中实现直接将日志写入redis**<br />docker ——》redis ——》Logstash——》Elasticsearch<br />通过代码层面,直接将日志写入redis,最后写入 Elasticsearch。<br />以上就是对 Docker 日志的所有的概念解释和方提供,具体采用什么方案,根据公司的具体的业务来选择。合适的才是最好的。
<a name="ZQxHb"></a>
## dockerignore
**简介**<br />.dockerignore 文件的作用类似于 git 工程中的 .gitignore 。不同的是 .dockerignore 应用于 docker 镜像的构建,它存在于 docker 构建上下文的根目录,用来排除不需要上传到 docker 服务端的文件或目录。<br />docker 在构建镜像时首先从构建上下文找有没有 .dockerignore 文件,如果有的话则在上传上下文到 docker 服务端时忽略掉 .dockerignore 里面的文件列表。这么做显然带来的好处是:

- 构建镜像时能避免不需要的大文件上传到服务端,从而拖慢构建的速度、网络带宽的消耗;
- 可以避免构建镜像时将一些敏感文件及其他不需要的文件打包到镜像中,从而提高镜像的安全性;

**.dockerignore 文件编写方法**<br />`.dockerignore` 文件的写法和 `.gitignore` 类似,支持正则和通配符,具体规则如下:

- 每行为一个条目;
- 空行被忽略;
- 构建上下文路径为所有文件的根路径;
<a name="85IgX"></a>
##### .dockerignore匹配规则:
| 符号 | 作用 |
| --- | --- |
| # | 注释 |
| * | 匹配0或多个非/的字符 |
| ? | 匹配1个非/的字符 |
| ** | 0个或多个目录 |
| ! | 除...外,需结合上下文语义 |

<a name="5UPda"></a>
##### 文件匹配规则具体语法如下:
| 规则 | 行为 |
| :---: | :---: |
| */temp* | 匹配根路径下一级目录下所有以 temp 开头的文件或目录 |
| */*/temp* | 匹配根路径下两级目录下所有以 temp 开头的文件或目录 |
| temp? | 匹配根路径下以 temp 开头,任意一个字符结尾的文件或目录 |
| **/*.go | 匹配所有路径下以 .go 结尾的文件或目录,即递归搜索所有路径 |
| *.md!<br />README.md | 匹配根路径下所有以 .md 结尾的文件或目录,但 README.md 除外 |

**注意事项:**<br />如果两个匹配语法规则有包含或者重叠关系,那么以后面的匹配规则为准,比如:

.md !README.md README-secret.md

这么写的意思是将根路径下所有以 .md 结尾的文件排除,以 README 开头 .md 结尾的文件保留,但是 README-secret.md 文件排除。<br />再来看看下面这种写法(同上面那种写法只是对换了后面两行的位置):

.md README-secret.md !README.md

这么写的意思是将根路径下所有以 .md 结尾和名称为 README-secret.md 的文件排除,但所有以 README 开头 .md 结尾的文件保留。这样的话 README-secret.md 依旧会被保留,并不会被排除,因为 README-secret.md 符合 !README*.md 规则。<br />**使用案例**<br />前段时间帮前端同学写了一个 Dockerfile,Dockerfile 放在 git 仓库根路径下,发现 git 工程中有很多真正应用跑起来用不到的文件,如果直接在 Dockerfile 中使用 COPY 或 ADD 指令拷贝文件,那么很显然会把很多不需要的文件拷贝到镜像中,从而会拖慢构建镜像的过程,产生的镜像也比较臃肿。解决方法就是编写 .dockerignore 文件,忽略掉不需要的文件,然后放到 docker 构建上下文的根路径下。.dockerignore 及 Dockerfile 文件内容如下:<br />**.dockerignore:**

.git _mockData deleted email-templates script static

**Dockerfile:**

FROM node:8-alpine COPY . /app/node WORKDIR /app/node RUN yarn install EXPOSE 8026 CMD [“yarn”, “run”, “tool-dev”]

使用 .dockerignore 前后上传到 docker 服务端的构建上下文大小对比:<br />使用前(73.36MB):

[Dreambeer@docker]$ docker build -t tool:5.0 -f Dockerfile-frontend-tool . Sending build context to Docker daemon 73.36MB Step 1/6 : FROM node:8-alpine

使用后(11.38MB):

[Dreambeer@docker]$ docker build -t tool:6.0 -f Dockerfile-frontend-tool . Sending build context to Docker daemon 11.38MB Step 1/6 : FROM node:8-alpine

<a name="zSYDw"></a>
##### .dockerignore普通示例:

comment

//temp temp? **/.md

<a name="yUrTa"></a>
##### .dockerignore排除示例1:

*.md !README.md

除README.md外,所有其他md文件都被docker忽略
<a name="6cHlu"></a>
##### .dockerignore排除示例2:

.md !README.md README-secret.md

除README*.md外,所有其他md文件(包括README-secret.md)都被docker忽略
<a name="WBQ5g"></a>
##### .dockerignore排除示例3:

.md README-secret.md !README.md

除README*.md外,所有其他md文件(不包括README-secret.md)都被docker忽略,即示例3第2行不起作用。
<a name="2HRUQ"></a>
## 在构建镜像查看各层大小(docker history image)

docker history Nginx -Dockerfile编写分享 - 图6

可以用来查看构建镜像时各个层构建大小

<a name="5hmXq"></a>
## ![image.png](https://cdn.nlark.com/yuque/0/2021/png/21527842/1624177815557-495677ed-c09d-48ad-94c7-484aec016ac7.png#align=left&display=inline&height=158&margin=%5Bobject%20Object%5D&name=image.png&originHeight=158&originWidth=711&size=23841&status=done&style=none&width=711)
<a name="FPiaE"></a>
## 缩小构建层数
可参照如下两篇博文<br />[如何快速将容器云镜像大小精简98%?](https://mp.weixin.qq.com/s/LOXNMYtZbnYeDR2lBI56fw)<br />[Docker Image Optimization: from 1.16GB to 22.4MB](https://medium.com/the-agile-crafter/docker-image-optimization-from-1-16gb-to-22-4mb-53fdb4c53311)
<a name="lMPUs"></a>
# Nginx的多阶段构建

This is DockerFile based on the alpine image

Stage 1

FROM alpine:3.14.0 as build LABEL MAINTAINER=”zhang.kai@chzh.cn” ENV PATH $PATH:/usr/local/nginx/sbin/ ENV LANG “en_US.UTF-8” ENV NGINX_VERSION 1.14.2 ARG CPU_NUM

修改源

RUN echo “http://mirrors.aliyun.com/alpine/latest-stable/main/“ > /etc/apk/repositories && \ echo “http://mirrors.aliyun.com/alpine/latest-stable/community/“ >> /etc/apk/repositories && \ apk —no-cache update && \ apk add —no-cache tzdata bash && \ apk add —no-cache gcc libc-dev zlib-dev pcre-dev make openssl-dev wget && \

创建用户创建用户组

addgroup -S www && \
adduser  -s /sbin/nologin -S -D -G www www && \

创建用户目录赋权绑定

mkdir -p /data/www && \
chown -R www:www /data/www && \

编译安装

wget -P /usr/local/ http://downloads.ichzh.com/Nginx/nginx-$NGINX_VERSION.tar.gz && \
cd /usr/local/ && \
tar -zxvf nginx-$NGINX_VERSION.tar.gz && \
cd /usr/local/nginx-$NGINX_VERSION && \
./configure --user=www \ 
--group=www \
--prefix=/usr/local/nginx \
--with-http_stub_status_module \
--without-http-cache \
--with-http_ssl_module \
--sbin-path=/usr/local/nginx/sbin/nginx \
--conf-path=/usr/local/nginx/conf/nginx.conf \
--error-log-path=/var/log/nginx_error.log \
--http-log-path=/var/log/nginx_access.log \
--pid-path=/usr/local/nginx/run/nginx.pid \
--lock-path=/usr/local/nginx/run/nginx.lock \
--with-compat \
--with-threads \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_dav_module \
--with-http_gzip_static_module \
--with-http_mp4_module \
--with-http_random_index_module \
--with-http_realip_module \
--with-http_secure_link_module \
--with-http_slice_module \
--with-http_sub_module \
--with-http_v2_module \
--with-mail \
--with-mail_ssl_module \
--with-stream \
--with-stream_realip_module \
--with-stream_ssl_module \
--with-stream_ssl_preread_module && \
make -j$CPU_NUM && make install && \
#清理运行时不需要的软件和安装缓存
rm -rf /var/cache/apk/* && \
rm -rf /root/.cache && \
rm -rf /tmp/* && \
rm -rf /usr/local/nginx-$NGINX_VERSION && \
rm -rf /usr/local/nginx-$NGINX_VERSION.tar.gz && \
apk del tzdata bash && \
apk del gcc libc-dev zlib-dev make openssl-dev wget

添加本地配置文件

ADD nginx.conf /usr/local/nginx/conf/

Stage 2: Serve the application from Alpine

FROM alpine:3.14.0 COPY —from=build /usr/local/nginx /usr/local/ COPY —from=build /usr/local/nginx/conf/ /usr/local/nginx/conf/ COPY —from=build /usr/local/nginx/run/ /usr/local/nginx/run/ ENV PATH $PATH:/usr/local/nginx/sbin/ ENV LANG “en_US.UTF-8” RUN echo “http://mirrors.aliyun.com/alpine/latest-stable/main/“ > /etc/apk/repositories && \ echo “http://mirrors.aliyun.com/alpine/latest-stable/community/“ >> /etc/apk/repositories && \ apk add —no-cache pcre-dev net-tools vim && \ addgroup -S www && \ adduser -s /sbin/nologin -S -D -G www www && \ ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ ln -sf /dev/stdout /var/log/nginx_access.log && \ ln -sf /dev/stderr /var/log/nginx_error.log EXPOSE 80 WORKDIR /usr/local/nginx CMD [“nginx”,”-g”,”daemon off;”]

docker run -it -d —name nginx -v /Docker/nginx-jalpine-image/data:/usr/local/nginx/html/ -p 81:80 —rm nginx-1.20.1:alpine

docker run -it -d —name nginx -p 81:80 —rm nginx-1.20.1:alpine

docker build -t nginx:alpine —build-arg CPU_NU=<机器CPU核数> ./

查看核数”cat /proc/cpuinfo | grep processor | wc -l”

```