Docker 学习 从零开始的 docker 部署

2018-12-21 1730点热度 1人点赞

Docker 作为一种新兴的虚拟化容器部署方式,相较于传统虚拟化更为轻便,并且由于 docker 封装的方式,使其可以不受依赖的限制将应用运行在任何系统中。


Docker 是个划时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的维护效率,降低了云计算应用开发的成本!使用 Docker,可以让应用的部署、测试和分发都变得前所未有的高效和轻松!

本文引用书籍 《Docker 从入门到实践》 下载链接


Docker 简介

Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。 Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)

Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目已经超过 4 万 6 千个星标和一万多个 fork 。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker 。 Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker 。

Docker 现在分为 CE 和 EE 两大版本。 CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。 Docker CE 分为 stable, test, 和 nightly 三个更新频道。每六个月发布一个 stable 版本 (18.03, 18.09, 19.03…) 。 较旧版本的 Docker 被称为 docker (docker-engine) ,其版本停止在 Docker 1.13.1 并且不再更新。现在大家经常说的 Docker 实际上为 Docker CE


1 、 Docker 基本概念

Docker 包括三个基本概念,镜像(Image)容器(Container)仓库(Repository)

Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。镜像的实际体现是由一组文件系统组成,或者说,由多层文件系统联合组成。镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层,不会对上一层有任何的改变。

Docker 容器 的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。

Docker 仓库 是一个提供集中的存储、分发镜像的服务。一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 < 仓库名>:< 标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。


2 、 Docker 安装

Docker 官方提供了各种环境下的 安装指南 本文只讲解 CentOS 如何安装 Docker CE

2.1 、卸载旧版本

较旧版本的 Docker 被称为 dockerdocker-engine 。使用以下命令卸载旧版本。

sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine

您可以根据需要以不同方式安装 Docker CE:

  • 大多数用户使用 Docker 的仓库 安装 Docker CE,同时便于后期的管理和升级,这是推荐的方法。
  • 有些用户下载 RPM 软件包进行 手动安装 同时完全手动的进行后期管理和升级。这在例如无法互联网的隔离系统上安装 Docker 的情况下非常有用。
  • 在测试和开发环境中,一些用户选择使用自动 脚本 来安装 Docker 。

2.2 、 CentOS 使用 yum 源安装

安装所需的包。 yum-utils 提供了 yum-config-manager 安装源的方式。并且 devicemapper 存储驱动程序需要 device-mapper-persistent-datalvm2
(CentOS/RHEL 在 Docker CE 18.09 和之后的版本中默认使用的是 overlay2 驱动)

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

使用以下命令设置 Docker 源。

sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

(可选)使用以下命令设置国内 Docker 源。(中国科学技术大学)

sudo yum-config-manager --add-repo http://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.ustc.edu.cn\/docker-ce/g' docker-ce.repo

更新 yum 软件源缓存,并安装 Docker CE 。

sudo yum makecache fast
sudo yum install docker-ce

配置开机启动 Docker CE,并启动 Docker CE

sudo systemctl enable docker
sudo systemctl start docker

默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket 。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

(可选)创建 docker 用户组并将当前用户加入 docker 组

sudo groupadd docker
sudo usermod -aG docker $USER

2.3 、测试 Docker 运行

(需退出当前终端并重新登录)

[root@service ~]# docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:

The Docker client contacted the Docker daemon.

The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)

The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.

The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

若能正常输出以上信息,则说明安装成功。

(可选)CentOS 运行 Docker 出现 net.bridge 警告

默认配置下,如果在 CentOS 使用 Docker CE 运行 docker info 看到下面的这些警告信息:

WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

请添加内核配置参数以启用这些功能。

sudo tee -a /etc/sysctl.conf <<-EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

然后重新加载 sysctl.conf 即可

sudo sysctl -p

(可选)配置国内 Docker Hub 镜像加速器

/etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)

{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn/"
  ]
}

之后重新启动 Docker 服务。

sudo systemctl daemon-reload
sudo systemctl restart docker

(可选)CentOS 运行 Docker 出现转发警告

默认配置下,如果在 CentOS 使用 Docker CE 创建新容器时看到下面的这些警告信息:

WARNING: IPv4 forwarding is disabled. Networking will not work.

请添加内核配置参数以启用这些功能。

sudo tee -a /etc/sysctl.conf <<-EOF
net.ipv4.ip_forward = 1
EOF

然后重新加载 sysctl.conf 即可

sudo sysctl -p

3 、 Docker 运行

3.1 、获取镜像

从 Docker 镜像仓库获取镜像的命令是 docker pull 。其命令格式为:

docker pull [选项] [Docker Registry 地址 [: 端口号]/] 仓库名 [: 标签]

具体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。

Docker 镜像仓库地址:地址的格式一般是 < 域名/IP>[: 端口号] 。默认地址是 Docker Hub 。
仓库名:如之前所说,这里的仓库名是两段式名称,即 < 用户名>/< 软件名> 。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。

[root@service ~]# docker pull ubuntu:18.04

18.04: Pulling from library/ubuntu
bf5d46315322: Pull complete
9f13e0ac480c: Pull complete
e8988b5b3097: Pull complete
40af181810e7: Pull complete
e6f7c7e5c03e: Pull complete
Digest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbe
Status: Downloaded newer image for ubuntu:18.04

上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 ubuntu:18.04,因此将会获取官方镜像 library/ubuntu 仓库中标签为 18.04 的镜像。

当然,出于某种原因我们会尝试自制镜像,与其对应的两条命令分别是

保存镜像到文件
[root@service ~]# docker save myimage:v1 > myimage_v1.tar

从文件加载镜像
[root@service ~]# docker load < myimage_v1.tar

3.2 、查看镜像

要想查看已经下载下来的镜像,可以使用 docker image list 命令。

[root@service ~]# docker image list

REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
redis        latest    5f515359c7f8   5 days ago       183 MB
nginx        latest    05a60462f8ba   5 days ago       181 MB
mongo        3.2       fe9198c04d62   5 days ago       342 MB
<none>       <none>    00285df0df87   5 days ago       342 MB
ubuntu       18.04     f753707788c5   4 weeks ago      127 MB
ubuntu       latest    f753707788c5   4 weeks ago      127 MB
ubuntu       14.04     1e0c3dd64ccd   4 weeks ago      188 MB

列表包含了 仓库名、标签、镜像 ID 、创建时间 以及 所占用的空间。

其中仓库名、标签在之前的基础概念章节已经介绍过了。镜像 ID 则是镜像的唯一标识,一个镜像可以对应多个标签。因此,在上面的例子中,我们可以看到 ubuntu:18.04 和 ubuntu:latest 拥有相同的 ID,因为它们对应的是同一个镜像。

注意,docker image list 查看的所占用空间和在 Docker Hub 上看到的镜像大小不同。比如,ubuntu:18.04 镜像大小,在这里是 127 MB,但是在 Docker Hub 显示的却是 50 MB 。这是因为 Docker Hub 中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而 docker image list 显示的是镜像下载到本地后,展开的大小,准确说,是展开后的各层所占空间的总和,因为镜像到本地后,查看空间的时候,更关心的是本地磁盘空间占用的大小。

另外一个需要注意的问题是,docker image list 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。

你可以通过以下命令来便捷的查看镜像、容器、数据卷所占用的空间。

[root@service ~]# docker system df

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          24        0         1.992GB   1.992GB (100%)
Containers      1         0         62.82MB   62.82MB (100%)
Local Volumes   9         0         652.2MB   652.2MB (100%)
Build Cache                         0B        0B

如果要删除本地的镜像,可以使用 docker image rm 命令,其格式为:

docker image rm [选项] < 镜像 1> [< 镜像 2> …]

其中,< 镜像> 可以是 镜像短 ID 、镜像长 ID 、镜像名 或者 镜像摘要。如下例

[root@service ~]# docker image list

REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
redis        latest    5f515359c7f8   5 days ago       183 MB
nginx        latest    05a60462f8ba   5 days ago       181 MB
mongo        3.2       fe9198c04d62   5 days ago       342 MB
<none>       <none>    00285df0df87   5 days ago       342 MB
ubuntu       18.04     f753707788c5   4 weeks ago      127 MB
ubuntu       latest    f753707788c5   4 weeks ago      127 MB
ubuntu       14.04     1e0c3dd64ccd   4 weeks ago      188 MB

我们可以用镜像的完整 ID,也称为 长 ID,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用 短 ID 来删除镜像。 docker image list 默认列出的就已经是短 ID 了,一般取前 3 个字符以上,只要足够区分于别的镜像就可以了。


3.3 、运行容器

有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 ubuntu:18.04 为例,如果我们打算启动里面的 bash 并且进行交互式操作的话,可以执行下面的命令。

[root@service ~]# docker run -it --rm ubuntu:18.04 bash

root@e7009c6ce357:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.1 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.1 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

docker run 就是运行容器的命令,具体格式我们会在 容器 一节进行详细讲解,我们这里简要的说明一下上面用到的参数。

-it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
--rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm 。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
ubuntu:18.04:这是指用 ubuntu:18.04 镜像为基础来启动容器。
bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash 。

进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 cat /etc/os-release,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 Ubuntu 18.04.1 LTS 系统。

最后我们通过 exit 退出了这个容器。


3.4 、 Docker 管理

创建容器并运行

docker run [OPTION] [IMAGE NAME] [COMMAND]

-i 则让容器的标准输入保持打开。由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。
-t 选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上(常见用法为 it 参数同时使用)
-d 让容器在后台运行
-p 手动映射容器端口
-P 自动映射容器端口
-v 挂载主机目录到容器 (目录不存在则创建)
--mount 挂载主机目录到容器 (目录不存在则报错)
--name 设置容器实例名称
--restart 配置容器自动重启方式
--rm 容器退出后随之将其删除

查看容器信息

docker container ls -a

查看容器的输出信息

docker container log [container ID or NAMES]

对于容器的启动、停止、重启、删除

docker container start [container ID or NAMES]
docker container stop [container ID or NAMES]
docker container restart [container ID or NAMES]
docker container rm [container ID or NAMES]

进入后台运行的容器进程(A 方式)

docker attach [container ID or NAMES]

[注意] 从这个 stdin 中 exit,会导致容器的停止

进入后台运行的容器进程(B 方式)

docker exec [container ID or NAMES]

-i 和 -t 参数等价于 docker run -it 的含义
从这个 stdin 中 exit,不会导致容器的停止。

导出容器与导入容器

[root@service ~]# docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                    PORTS               NAMES
7691a814370e        ubuntu:18.04        "/bin/bash"         36 hours ago        Exited (0) 21 hours ago                       test

docker export 7691a814370e > ubuntu.tar
cat ubuntu.tar | docker import - test/ubuntu:v1.0

关闭全部容器进程并删除

docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)

一键清理所有关闭的容器、关联失效的镜像、没有挂载的数据卷

docker container prune
docker image prune
docker volume prune

查看容器详细信息和制作命令

docker inspect alpine:3.9

[
    {
        "Id": "sha256:78a2ce922f8665f5a227dc5cd9fda87221acba8a7a952b9665f99bc771a29963",
        "RepoTags": [
            "alpine:3.9"
        ],
        "RepoDigests": [
            "alpine@sha256:414e0518bb9228d35e4cd5165567fb91d26c6a214e9c95899e1e056fcd349011"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2020-04-24T01:05:35.807646609Z",
        "Container": "820d0121fc3f91dfd4d488ca1dbb8668b05da3387c8f90e22b6528fbcd38c8db",
        "ContainerConfig": {
            "Hostname": "820d0121fc3f",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "CMD [\"/bin/sh\"]"
            ],
            "ArgsEscaped": true,
            "Image": "sha256:186eda4636e895d982896312666e472a2d62aab1490608701e1b3438ac6649e7",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "DockerVersion": "18.09.7",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/sh"
            ],
            "ArgsEscaped": true,
            "Image": "sha256:186eda4636e895d982896312666e472a2d62aab1490608701e1b3438ac6649e7",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 5546112,
        "VirtualSize": 5546112,
        "GraphDriver": {
            "Data": {
                "MergedDir": "/var/lib/docker/overlay2/a2c3d6432b87a415387c0c038c432cea7da7003d7daa3c2ae23dd1a0c8c6af3c/merged",
                "UpperDir": "/var/lib/docker/overlay2/a2c3d6432b87a415387c0c038c432cea7da7003d7daa3c2ae23dd1a0c8c6af3c/diff",
                "WorkDir": "/var/lib/docker/overlay2/a2c3d6432b87a415387c0c038c432cea7da7003d7daa3c2ae23dd1a0c8c6af3c/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:89ae5c4ee501a09c879f5b58474003539ab3bb978a553af2a4a6a7de248b5740"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

使用 Dockerfile 制作 镜像

参考文件例如 [ 链接 ] [ 链接 ] [ 链接 ]

cd /opt/tempimage/
touch Dockerfile

#修改好后即可制作
docker build -t tempimage:v1 .

参考样例如下 LNMP + Wordpress

mkdir ./www/
wget https://wordpress.org/latest.zip -O ./www/latest.zip
cd ./www/
unzip latest.zip 
mv wordpress html
cd ../
docker build -t wp:v1 .
文件 ./www/Dockerfile
FROM alpine:3.18
# Install package
RUN set -ex; \
    apk --update upgrade --no-cache; \
    apk add --no-cache \
        curl \
        zlib \
        ca-certificates \
        openssl \
        tar \
        nano \
        libmemcached \
        imagemagick-dev \
        nginx \
        mariadb \
        mariadb-client \
        php82 \
        php82-bcmath \
        php82-common \
        php82-curl \
        php82-fpm \
        php82-gd \
        php82-gmp \
        php82-pecl-igbinary \
        php82-pecl-imagick \
        php82-imap \
        php82-intl \
        php82-json \
        php82-ldap \
        php82-mbstring \
        php82-pecl-memcached \
        php82-pecl-msgpack \
        php82-pecl-redis \
        php82-mysqli \
        php82-opcache \
        php82-openssl \
        php82-pdo \
        php82-pdo_mysql \
        php82-xml \
        php82-zip \
        composer \
        redis \
        supervisor \
        tzdata \
    && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone \
    && apk del tzdata \
    && addgroup mysql mysql \
    && mkdir -p /www/html \
    && mkdir -p /www/mysql \
    && mkdir -p /www/config \
    && mkdir -p /www/logs/nginx \
    && mkdir -p /www/logs/mysql \
    && mkdir -p /www/logs/php\
    && chown -R nginx:nginx /www/logs/nginx \
    && chown -R nginx:nginx /www/logs/php \
    && chown -R mysql:mysql /www/logs/mysql 

# Copy File
COPY --chown=nginx:nginx ./www/html /www/html
COPY --chown=root:root ./www/config /www/config
    
# Config LNMP
RUN set -ex; \
    mkdir -p /etc/supervisor/conf.d/ \
    && cp /www/config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf \
    && cp /www/config/nginx.conf /etc/nginx/nginx.conf \
    && mkdir -p /etc/nginx/conf.d/ \
    && cp /www/config/default.conf /etc/nginx/conf.d/default.conf \
    && sed -i "s/;date.timezone =.*/date.timezone = Asia\/Shanghai/g" /etc/php82/php.ini \
    && sed -i "s/expose_php = On/expose_php = Off/g" /etc/php82/php.ini \
    && sed -i "s/;error_log = php_errors.log/error_log = \/www\/logs\/php\/php82_error.log/g" /etc/php82/php.ini \
    && sed -i "s/memory_limit = 128M/memory_limit = 512M/g" /etc/php82/php.ini \
    && sed -i "s/upload_max_filesize =.*/upload_max_filesize = 100M/g" /etc/php82/php.ini \
    && sed -i "s/post_max_size =.*/post_max_size = 128M/g" /etc/php82/php.ini \
    && sed -i "s/user = nobody/user = nginx/g" /etc/php82/php-fpm.d/www.conf \
    && sed -i "s/group = nobody/group = nginx/g" /etc/php82/php-fpm.d/www.conf \
    && sed -i "s/;opcache.enable=.*/opcache.enable=1/g" /etc/php82/php.ini \
    && sed -i "s/listen = 127.0.0.1:9000/listen = 0.0.0.0:9000/g" /etc/php82/php-fpm.d/www.conf \
    && sed -i "s/listen.owner = nobody/listen.owner = nginx/g" /etc/php82/php-fpm.d/www.conf \
    && sed -i "s/listen.group = nobody/listen.group = nginx/g" /etc/php82/php-fpm.d/www.conf \
    && mysql_install_db --user=mysql --datadir=/www/mysql \  
    && (mysqld_safe --datadir='/www/mysql' &) \ 
    && sleep 5 \ 
    && mysql -u root -e "CREATE DATABASE temp;" \ 
    && mysql -u root -e "CREATE USER 'dbuser'@'localhost' IDENTIFIED BY '123456'; GRANT ALL ON temp.* TO 'dbuser'@'localhost'; FLUSH PRIVILEGES;" \ 
    && mysqladmin -u root password P@ssw0rd \
    && killall -TERM mysqld_safe \
    && sleep 3

#    && mysql -u root -D temp < DBdump.sql \

# Add application
VOLUME /www/
WORKDIR /www/

EXPOSE 80 9000
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
文件 ./www/config/supervisord.conf
; supervisor config file

[supervisord]
nodaemon=true
logfile=/www/logs/supervisord.log

[program:php-fpm]
command=php-fpm82 -F -R
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
priority=20
autorestart=false
startretries=0

[program:nginx]
command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;'
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
priority=50
autorestart=false
startretries=0

[program:mysql]
command=/usr/bin/mysqld_safe --datadir='/www/mysql' --log-error=/www/logs/mysql/mysql.err --pid-file=/www/mysql/mysql.pid
user=mysql
priority=0
autorestart=false
startretries=0
文件 ./www/config/nginx.conf
user nginx;

worker_processes auto;
worker_cpu_affinity auto;

pcre_jit on;

error_log /www/logs/nginx/nginx_error.log warn;
pid /www/nginx.pid;

include /etc/nginx/modules/*.conf;

events {
        worker_connections 1024;
        multi_accept on;
}

http {
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
        server_tokens off;
        client_max_body_size 100m;
        keepalive_timeout 65;
        sendfile on;
        tcp_nodelay on;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:2m;
        gzip_vary on;
        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 /www/logs/nginx/access.log main;
        error_log /dev/stderr notice;
        include /etc/nginx/conf.d/*.conf;
}
文件 ./www/config/conf.d/default.conf
server {
    listen 80 default_server;
    server_name 127.0.0.1 _;
    root /www/html;
    access_log  /www/logs/nginx/wp.log;
    error_log  /www/logs/nginx/wp_error.log;
    
    location / {
        try_files $uri $uri/ /index.php?$args;
        index index.php index.html index.htm;
    }
    
    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
        fastcgi_intercept_errors on;
    }

    location ~* \.(js|css|gif|jpg|jpeg|png|bmp|ico)$ {
        expires   max;
        error_log /dev/null;
        access_log off;
        log_not_found off;
    }

}

StarryVoid

Have a good time