ithuang
ithuang
发布于 2026-06-03 / 2 阅读
0

私有仓库与dockerfile

1.镜像仓库搭建与镜像推送

1.1镜像仓库

镜像仓库是用于 存储、管理和分发 Docker 镜像 的集中化服务,类似代码仓库(如 gitlab/github),但专门针对容器镜像。

核心功能

  • 存储镜像的各个分层(Layers)。
  • 支持镜像的版本管理(通过 Tag)。
  • 提供访问控制(权限管理)。

【掌握】仓库分类

类型特点代表产品公共仓库- 免费或有限免费
- 无需自维护
- 适合开源项目或个人开发Docker Hub、GitHub Container Registry (GHCR)、Quay.io、阿里云镜像服务私有仓库- 企业内网部署
- 完全控制数据和权限
- 需自行维护Harbor(哈勃)、Docker Registry(官方开源)云托管仓库- 云厂商提供托管服务
- 深度集成云原生工具链
- 按需付费AWS ECR、Google Artifact Registry、Azure Container Registry

1.2镜像仓库搭建

方案一:Docker Registry是一个官方开源的仓库

渡渡鸟镜像地址:https://docker.aityp.com/

02_私有仓库与dockerfile1.png

先拉取镜像,从渡渡鸟上拉取的
[root@web01 ~]# docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/registry:2

运行容器
[root@web01 ~]# docker run -d \
    --restart=always \
    -p 5000:5000 \
    --name private-registry \
    -v /opt/registry:/var/lib/registry \
    swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/registry:2


修改/etc/docker/daemon.json配置文件,允许非 HTTPS 访问,
{
  "insecure-registries": ["192.168.88.114:5000"],      # 换成自己的 IP
  "registry-mirrors":[ 
    "https://docker.1panelproxy.com",
    "https://hub.openeeds.com",
    "https://dockerproxy.1panel.live",
    "https://docker.1panel.live",
    "https://proxy.1panel.live",
    "https://docker.m.daocloud.io",
    "https://noohub.ru",
    "https://huecker.io",
    "https://dockerhub.timeweb.cloud",
    "https://0c105db5188026850f80c001def654a0.mirror.swr.myhuaweicloud.com",
    "https://5tqw56kt.mirror.aliyuncs.com",
    "https://docker.1panel.live",
    "http://mirrors.ustc.edu.cn/",
    "http://mirror.azure.cn/",
    "https://hub.rat.dev/",
    "https://docker.ckyl.me/",
    "https://docker.chenby.cn",
    "https://docker.hpcloud.cloud",
    "https://docker.m.daocloud.io"
   ]
}
重新加载配置文件并重启服务
[root@web01 ~]# systemctl daemon-reload && systemctl restart docker
  • 推送镜像到镜像仓库
[root@web01 ~]#  docker inspect nginx:latest

推送镜像到仓库,先重新给镜像打tag
[root@web01 ~]#  docker tag   nginx:latest   192.168.88.101:5000/nginx:latest

推送镜像到仓库
[root@web01 ~]#  docker push 192.168.88.101:5000/nginx:latest

查看是否推送成功
[root@web01 ~]# curl http://192.168.88.101:5000/v2/_catalog
{"repositories":["nginx"]}

02_私有仓库与dockerfile2.png

[作业] 方案二:使用阿里云镜像仓库

登录阿里云账号,主页面上找产品-->容器--> 容器镜像服务-->然后使用账号登录

02_私有仓库与dockerfile3.png

02_私有仓库与dockerfile4.png

02_私有仓库与dockerfile5.png

02_私有仓库与dockerfile6.png

02_私有仓库与dockerfile7.png

02_私有仓库与dockerfile8.png

登录成功后,做下列操作

1, 创建命名空间(命名空间和镜像仓库名称合到一起组成镜像的路径名称)

02_私有仓库与dockerfile9.png

02_私有仓库与dockerfile10.png

2.创建镜像仓库,指定仓库名称

02_私有仓库与dockerfile11.png

02_私有仓库与dockerfile12.png

02_私有仓库与dockerfile13.png

02_私有仓库与dockerfile14.png

登录私有仓库前操作:设置访问凭据(要接收验证码)

02_私有仓库与dockerfile15.png

02_私有仓库与dockerfile16.png

早期docker login登录镜像仓库使用阿里云登录密码,最近版本更新后,要求增加一个固定密码,必须按照以上设置,否则无法登录仓库

登录阿里云镜像仓库

02_私有仓库与dockerfile17.png

登录示例:

[root@web01 ~]#  docker login --username=苍狼北城 crpi-6f1eajr6er1b4zk0.cn-hangzhou.personal.cr.aliyuncs.com
Password:   #输入访问凭据设置的固定密码登录

WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.  #凭据在本机的保存位置
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded
[root@web01 ~]#

给本地镜像打Tag标签,如果没有Tag无法上传

02_私有仓库与dockerfile18.png

推送镜像示例:

[root@web01 ~]# docker tag 5aca99593157 crpi-6f1eajr6er1b4zk0.cn-hangzhou.personal.cr.aliyuncs.com/private_ns/private_repo:nginx-latest
[root@web01 ~]# docker images
                                                                            i Info →   U  In Use
IMAGE                                            ID             DISK USAGE   CONTENT SIZE   EXTRA
192.168.88.101:5000/nginx:latest                 5aca99593157        238MB           66MB    U
crpi-6f1eajr6er1b4zk0.cn-hangzhou.personal.cr.aliyuncs.com/private_ns/private_repo/nginx:latest
                                                 5aca99593157        238MB           66MB    U
crpi-6f1eajr6er1b4zk0.cn-hangzhou.personal.cr.aliyuncs.com/private_ns/private_repo:nginx-latest
                                                 5aca99593157        238MB           66MB    U
nginx:latest                                     5aca99593157        238MB           66MB    U
swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/registry:2
                                                 2e3dacd68b23       36.3MB         10.1MB    U

[root@web01 ~]# docker push crpi-6f1eajr6er1b4zk0.cn-hangzhou.personal.cr.aliyuncs.com/private_ns/private_repo:nginx-latest
The push refers to repository [crpi-6f1eajr6er1b4zk0.cn-hangzhou.personal.cr.aliyuncs.com/private_ns/private_repo]
13fd728be9eb: Pushed
5b4d6ff92fc4: Pushed
830625e1ac85: Pushed
5431d0092ffd: Pushed
b4a248c845e5: Pushed
7f8b1a2b17d8: Pushed
45381ecb0e2f: Pushed
nginx-latest: digest: sha256:0a0b02fec34ea28aac0f7e0cf8403e5c9ee5fc201162eae5bf891a84e5599281 size: 2290

i Info → Not all multiplatform-content is present and only the available single-platform image was pushed
          sha256:5aca99593157f4ae539a5dec1092a0ad8762f8e2eb1789085a13a0f5622369f6 -> sha256:0a0b02fec34ea28aac0f7e0cf8403e5c9ee5fc201162eae5bf891a84e5599281
[root@web01 ~]#

到阿里云查看推送结果

02_私有仓库与dockerfile19.png

2.【重点】镜像管理

2.1查看本地镜像

通过docker images命令查看当前镜像列表; 使用man docker-images得到参数说明

[root@web01~]# docker images

2.2拉取镜像(重点)

通过docker pull拉取(下载)镜像。

[root@web01 ~]# docker pull moongeun/centos9
查看已经拉取的镜像
[root@web01 ~]# docker images
REPOSITORY                  TAG       IMAGE ID       CREATED         SIZE
mysql                       latest    245a6c909dc0   6 days ago      921MB
192.168.88.114:5000/nginx   latest    2cd1d97f893f   2 weeks ago     192MB
nginx                       latest    2cd1d97f893f   2 weeks ago     192MB
192.168.88.114/nginx        latest    2cd1d97f893f   2 weeks ago     192MB
registry                    2         26b2eb03618e   22 months ago   25.4MB
moongeun/centos9            latest    7789ec4a4b42   2 years ago     520MB

2.3删除镜像(重点)

通过docker rmi删除镜像; man docker-rmi查看参数帮助

[root@web01 ~]# docker rmi moongeun/centos9
rmi:两个单词合成,remove移除,image镜像

或者
[root@web01 ~]# docker image rm moongeun/centos9

2.4镜像导出

使用docker save保存(导出)镜像为一个tar文件

[root@web01 ~]# docker save -o /root/dockerimage_centos9.latest.tar.gz  moongeun/centos9:latest 

2.5镜像导入

使用docker load导入

测试时可以将导出的文件scp传输到另一台宿主机测试。或者先删除本地的镜像再导入测试

[root@web01 ~]# docker load -i /root/dockerimage_centos9.latest.tar.gz

2.6镜像重命名

可以通过公共仓库拉取镜像并重新打标签,再推送到私有仓库

[root@web01 docker]# docker images 
REPOSITORY                   TAG       IMAGE ID       CREATED         SIZE
mysql                        latest    245a6c909dc0   6 days ago      921MB
192.168.88.114/nginx         latest    2cd1d97f893f   2 weeks ago     192MB
192.168.88.114:5000/nginx    latest    2cd1d97f893f   2 weeks ago     192MB
nginx                        latest    2cd1d97f893f   2 weeks ago     192MB
registry                     2         26b2eb03618e   22 months ago   25.4MB
192.168.88.114:5000/centos   latest    7789ec4a4b42   2 years ago     520MB
moongeun/centos9             latest    7789ec4a4b42   2 years ago     520MB

[root@web01 ~]# docker tag moongeun/centos9  192.168.88.101:5000/centos:latest

推送镜像到私有仓库
[root@web01 ~]# docker push 192.168.88.101:5000/centos:latest

查看推送到私有仓库结果
[root@web01 ~]# curl http://192.168.88.114:5000/v2/_catalog
{"repositories":["centos","nginx"]}

注意:docker tag 不会复制镜像。它只是给同一个镜像ID增加一个新的名称(标签)。

2.7镜像清理(重点)

 [root@web01 ~]# docker image prune  # 交互式确认删除
 
 [root@web01 ~]# docker image prune -f  # 强制删除所有悬空镜像(没有仓库名(REPOSITORY)和标签(TAG)引用的镜像层)
 
 [root@web01 ~]# docker image prune -a  # 删除所有未被容器使用的镜像(谨慎!)
 
 [root@web01 ~]# docker image prune -a --filter "until=720h" # 删除超过30天的旧镜像
 
 [root@web01 ~]# docker images | grep "test" | awk '{print $3}' | xargs docker rmi -f # 删除特定模式的镜像(如包含"test"的镜像)
 
 
 注意:悬空镜像 只有id好没有名称和标签的镜像,如果不想删除docker  tag 重新打标签

3.镜像构建 Dockerfile

3.1 概述

02_私有仓库与dockerfile20.png

Docker 镜像是容器运行的 静态模板,本质是一个 分层的只读文件系统,包含:

  1. 基础环境:操作系统层(如 Alpine、Ubuntu)
  2. 应用依赖:运行时(Python、Node.js)、库文件
  3. 应用代码:编译后的二进制文件或脚本
  4. 配置信息:环境变量、启动命令、开放端口等元数据

Dockerfile本质上就是构建镜像文件的一个脚本文件,使用 Dockerfile 构建,标准化、可重复的镜像构建

3.2 Dockerfile文件核心语法

指令作用是否可多次使用说明/最佳实践FROM指定基础镜像(基于一个成熟的镜像做新的镜像)⚠️ 多阶段构建可多个普通 Dockerfile 通常只有一个,建议使用明确版本号RUN构建镜像时执行命令(执行容器内的命令)✅ 可以多条命令尽量用 && 合并,减少镜像层数COPY复制文件到镜像内✅ 可以推荐优先使用,比 ADD 更简单透明ADD复制文件(支持自动解压)✅ 可以仅在自动解压时使用,其他场景优先 COPYENV设置环境变量✅ 可以后面的值可覆盖前面的同名变量ARG定义构建参数✅ 可以通过 docker build --build-arg 传值WORKDIR设置默认工作目录可以的多个最终以最后一个目录为准USER指定容器运行的用户可以多个最终以最后一个用户为准EXPOSE声明容器服务的端口✅ 可以仅声明作用,不会自动开放端口,实际映射需用-p参数VOLUME定义匿名数据卷✅ 可以生产环境更推荐 docker run -v 显式挂载CMD默认启动参数唯一一个Dockerfile只应有一个CMD,容器默认的启动参数ENTRYPOINT默认启动命令(必须执行的主程序)唯一只有最后一个 ENTRYPOINT 生效,容器默认的启动命令

ENTRYPOINT 和CMD的区别

特性CMDENTRYPOINT作用提供容器启动时的 默认命令或参数定义容器启动时的 主命令是否可被 docker run 覆盖可以被覆盖不会被覆盖(除非用 --entrypoint 显式替换)常见用法给 ENTRYPOINT 提供默认参数定义容器必须执行的命令

3.3 四句真诀多用于编写dockerfile:

一、FROM起基础,WORKDIR定目录 【基础环境】

二、COPY放文件,RUN执行构建命令 【代码准备】

三、ENV设环境,EXPOSE开端口 【运行配置】

四、CMD启动程序,镜像构建成功 【启动命令】

语法案列(一)

# 编写dockerfile
[root@web01 ~]# mkdir myimg
[root@web01 ~]# vim myimg/dockerfile
FROM busybox:latest
CMD ["/bin/ls","-l"]

# 创建镜像
[root@web01 ~]# docker build -t mybusybox:latest myimg/
... ....

[root@web01 ~]# docker images
                                                                              i Info →   U  In Use
IMAGE                                              ID             DISK USAGE   CONTENT SIZE   EXTRA
192.168.88.101:5000/nginx:latest                   5aca99593157        238MB           66MB    U
busybox:latest                                     fd8d9aa63ba2       6.74MB         2.23MB    U
cf-workers-docker-io-38g.pages.dev/dokken/centos-stream-9:sha-ad271c6
                                                   0de27b1c1ffb        484MB          124MB
crpi-6f1eajr6er1b4zk0.cn-hangzhou.personal.cr.aliyuncs.com/private_ns/private_repo:nginx-latest
                                                   5aca99593157        238MB           66MB    U
maven:3.8-openjdk-17                               3a9c30b3af62       1.25GB          432MB
moongeun/centos9:latest                            004351fd9b4d        747MB          191MB
my_httpd:v1                                        09a6be9e5271        654MB          184MB
my_httpd:v2                                        8f95a8956eb3        654MB          184MB
my_tomcat:v1                                       19f0a5fa9c24       1.51GB          526MB
my_tomcat:v2                                       afbfa7e96330        1.1GB          322MB
mybusybox:latest                                   5ce3a3a9b053       6.74MB         2.23MB
             
 # 创建容器                                               
[root@web01 ~]# docker run -it --rm mybusybox:latest
total 20
drwxr-xr-x    2 root     root         12288 May 13 02:21 bin
drwxr-xr-x    5 root     root           360 Jun  1 04:07 dev
drwxr-xr-x    1 root     root            66 Jun  1 04:07 etc
drwxr-xr-x    2 nobody   nobody           6 May 13 02:21 home
drwxr-xr-x    2 root     root          4096 May 13 02:21 lib
lrwxrwxrwx    1 root     root             3 May 13 02:21 lib64 -> lib
dr-xr-xr-x  281 root     root             0 Jun  1 04:07 proc
drwx------    2 root     root             6 May 13 02:21 root
dr-xr-xr-x   13 root     root             0 Jun  1 04:07 sys
drwxrwxrwt    2 root     root             6 May 13 02:21 tmp
drwxr-xr-x    4 root     root            29 May 13 02:21 usr
drwxr-xr-x    4 root     root            30 May 13 02:21 var

# 传递参数命令,覆盖 CMD 执行
[root@web01 ~]# docker run -it --rm mybusybox:latest id
uid=0(root) gid=0(root) groups=0(root),10(wheel)
[root@web01 ~]#

思考:自己制作的镜像mybusybox为什么启动容器之后通过docker ps看不到容器?

02_私有仓库与dockerfile21.png

语法案列(二)

# ENTRYPOINT 与 CMD 执行方式为 ${ENTRYPOINT} ${CMD}
[root@web01 ~]# cat myimg/dockerfile
FROM busybox:latest
ENTRYPOINT ["echo"]
CMD ["/bin/ls","-l"]
[root@web01 ~]# docker build -t mybusybox2:latest myimg/
.... ...                            

# CMD 做为参数传递,在容器内执行了 echo '/bin/ls -l'
[root@web01 ~]# docker run -it --rm mybusybox2:latest
/bin/ls -l

# CMD 被替换,在容器内执行了 echo id
[root@web01 ~]# docker run -it --rm mybusybox2:latest id
id

02_私有仓库与dockerfile22.png

语法案列(三)

[root@web01 ~]# mkdir myimg3
[root@web01 ~]# cd myimg3
[root@web01 myimg3]# pwd
/root/myimg3
[root@web01 myimg3]# tar -czf myfile.tar -C /etc/ passwd hosts
[root@web01 myimg3]# ls
myfile.tar


[root@web01 ~]# vim myimg/dockerfile
FROM busybox:latest
COPY myfile.tar /var/tmp/
ADD  myfile.tar /tmp/
RUN  id && touch /tmp/file1
USER nobody
RUN  id && touch /tmp/file2
ENV  mymsg="Hello World"
WORKDIR /tmp
CMD  ["/bin/sh"]

[root@web01 ~]docker build -t mybusybox3:latest myimg/
.... ....                                 0.1s


[root@web01 ~]# docker run -it --rm mybusybox3:latest
# USER 指令设置使用 nobody 用户运行容器
/tmp $ id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)
0 directories, 0 files

# 使用 COPY 进来的文件还是 tar 包
/tmp $ ls /var/tmp/
myfile.tar

# 使用 ADD 添加的文件已经被解压了
/tmp $ ls -l /tmp/file?
-rw-r--r--    1 root     root             0 Jun  1 04:23 /tmp/file1
-rw-r--r--    1 nobody   nobody           0 Jun  1 04:23 /tmp/file2

# 环境变量可以直接调用
/tmp $ echo ${mymsg}
Hello World

# WORKDIR 把工作目录设置到 /tmp
/tmp $ pwd
/tmp
/tmp $ exit

使用nginx构建多阶段镜像

构建多阶段镜像

[root@docker ~]# mkdir nginx
[root@docker ~]# ls nginx
nginx-1.22.1.tar.gz 
[root@docker ~]# docker pull centos-stream-9:sha-ad271c6  #可以拉取或者导入镜像
[root@web01 nginx_all_one]# docker images
.... .....

[root@docker nginx]# vim nginx/Dockerfile
# 第一阶段编译程序(只负责编译)
FROM cf-workers-docker-io-38g.pages.dev/dokken/centos-stream-9:sha-ad271c6 as builder
ADD  nginx-1.22.1.tar.gz /
WORKDIR /nginx-1.22.1
RUN  dnf install -y openssl-devel pcre-devel gcc make zlib-devel
RUN  ./configure --prefix=/usr/local/nginx --with-pcre --with-http_ssl_module
RUN  make && make install
RUN  echo 'Nginx is running !' >/usr/local/nginx/html/index.html

# 第二阶段最终镜像
FROM cf-workers-docker-io-38g.pages.dev/dokken/centos-stream-9:sha-ad271c6
RUN  dnf install -y pcre openssl && dnf clean all 清理缓存
COPY --from=builder /usr/local/nginx /usr/local/nginx
ENV  PATH=${PATH}:/usr/local/nginx/sbin
WORKDIR /usr/local/nginx/html
EXPOSE 80/tcp
CMD  ["nginx", "-g", "daemon off;"]



[root@web01 nginx]#  docker build -t mynginx:v1 .


启动容器访问测试
[root@web01 nginx]# docker run -itd --rm --name mynginx mynginx:v1
09ee2d99c8f4dec04b0eb7b341eb1ed3127a67c760164f916a1e2b6322741514
[root@web01 nginx]# docker inspect mynginx | grep -i address
                    "IPAddress": "172.17.0.2",
                    "MacAddress": "12:d2:94:a2:82:dc",
                    "GlobalIPv6Address": "",
[root@web01 nginx]# curl 172.17.0.2
Nginx is running !

思考如果将多阶段镜像合并成一个:

[root@web01 ~]# cp -r nginx/ nginx_allinone
[root@web01 ~]# cd nginx_allinone
[root@web01 nginx_allinone]# ls
Dockerfile  nginx-1.22.1.tar.gz
[root@web01 nginx_allinone]# vim Dockerfile
FROM cf-workers-docker-io-38g.pages.dev/dokken/centos-stream-9:sha-ad271c6
ADD nginx-1.22.1.tar.gz /
WORKDIR /nginx-1.22.1
RUN dnf install -y gcc make pcre-devel openssl-devel zlib-devel
RUN ./configure --prefix=/usr/local/nginx --with-pcre --with-http_ssl_module
RUN make && make install
RUN echo 'Nginx is running !' >/usr/local/nginx/html/index.html
ENV PATH=${PATH}:/usr/local/nginx/sbin
WORKDIR /usr/local/nginx/html
EXPOSE 80
CMD ["nginx","-g","daemon off;"]

[root@web01 nginx_allinone]# docker build -t mynginxv2:v2 .
[root@web01 nginx_allinone]# docker images

结论:不对进行进行多阶段构建、那么制作出的镜像会很大、拉取和启动速度慢、效率低。

【作业】手工构建php-fpm镜像

以下案列是:基于一个Linux操作系统的镜像、安装一个php的服务、能够php在容器中启动

实验目标:

php-fpm 以普通用户(nobody)运行时,需要具备 PID 文件目录的写入权限。

[root@web01 ~]# docker run -itd --name myphp1 \
            cf-workers-docker-io-38g.pages.dev/dokken/centos-stream-9:sha-ad271c6 \
            /bin/bash
c8b28b3cb848f82477d2e0928ca16954a04544909e8a3bd78dad71130f73f8aa
[root@web01 ~]# docker ps

[root@web01 ~]# docker exec -it myphp4 /bin/bash
[root@c8b28b3cb848 /]# dnf -y install php
[root@c8b28b3cb848 /]# vi /etc/php-fpm.d/www.conf
38:  listen = 127.0.0.1:9000


[root@c8b28b3cb848 /]# chown -R nobody:nobody /var/log/php-fpm/
[root@c8b28b3cb848 /]# dnf -y install sudo

#普通用户执行命令报错
[root@c8b28b3cb848 /]# sudo -u nobody /bin/bash
bash-5.1$ /usr/sbin/php-fpm --nodaemonize
[01-Jun-2026 07:16:13] NOTICE: [pool www] 'user' directive is ignored when FPM is not running as root
[01-Jun-2026 07:16:13] NOTICE: [pool www] 'group' directive is ignored when FPM is not running as root
[01-Jun-2026 07:16:13] ERROR: Unable to create the PID file (/run/php-fpm/php-fpm.pid).: No such file or directory (2)
[01-Jun-2026 07:16:13] ERROR: FPM initialization failed



解决办法:

[root@c8b28b3cb848 /]# chown nobody:nobody /run/php-fpm
[root@c8b28b3cb848 /]# sudo -u nobody /bin/bash
bash-5.1$  /usr/sbin/php-fpm --nodaemonize
[01-Jun-2026 07:19:44] NOTICE: [pool www] 'user' directive is ignored when FPM is not running as root
[01-Jun-2026 07:19:44] NOTICE: [pool www] 'group' directive is ignored when FPM is not running as root
[01-Jun-2026 07:19:44] NOTICE: fpm is running, pid 178
[01-Jun-2026 07:19:44] NOTICE: ready to handle connections
[01-Jun-2026 07:19:44] NOTICE: systemd monitor interval set to 10000ms
^C[01-Jun-2026 07:25:37] NOTICE: Terminating ...
[01-Jun-2026 07:25:37] NOTICE: exiting, bye-bye!
bash-5.1$ exit
exit
[root@c8b28b3cb848 /]# exit
exit
[root@web01 ~]#

使用dockerfile构建镜像

[root@web01 ~]# docker cp myphp4:/etc/php-fpm.d/www.conf .
[root@web01 ~]# mkdir php
[root@web01 ~]# mv www.conf php/
[root@web01 ~]# vim php/dockerfile
FROM cf-workers-docker-io-38g.pages.dev/dokken/centos-stream-9:sha-ad271c6

# 1. 安装 PHP + FPM + sudo
RUN dnf -y install php php-fpm sudo && dnf clean all

# 2. 修改 php-fpm 配置文件、让他监听9000端口
COPY www.conf /etc/php-fpm.d/www.conf

# 3. 创建运行时目录(关键!)
RUN mkdir -p /run/php-fpm /var/log/php-fpm && \
    chown -R nobody:nobody /run/php-fpm /var/log/php-fpm

# 4. 切换用户(模拟你 sudo -u nobody)
USER nobody

# 5. 暴露端口(可选)
EXPOSE 9000

# 6. 前台启动 php-fpm(将这个服务作为容器的上帝进程来启动)
CMD ["/usr/sbin/php-fpm", "--nodaemonize"]


[root@web01 ~]#docker build -t php-fpm:latest php

验证镜像

[root@web01 ~]# docker run -itd --name myphp5 php-fpm:latest
ec9301245656ffb1a5a828e835a585247e5e18bd5e716597e08d251d97b7527a
[root@web01 ~]# docker exec -it myphp5 /bin/bash
bash-5.1$ id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)
bash-5.1$ ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
nobody         1       0  0 07:43 pts/0    00:00:00 php-fpm: master process (/etc/php-fpm.conf)
nobody         6       1  0 07:43 pts/0    00:00:00 php-fpm: pool www
nobody         7       1  0 07:43 pts/0    00:00:00 php-fpm: pool www
nobody         8       1  0 07:43 pts/0    00:00:00 php-fpm: pool www
nobody         9       1  0 07:43 pts/0    00:00:00 php-fpm: pool www
nobody        10       1  0 07:43 pts/0    00:00:00 php-fpm: pool www
nobody        11       0  0 07:43 pts/1    00:00:00 /bin/bash
nobody        18      11  0 07:43 pts/1    00:00:00 ps -ef
bash-5.1$ ss -antulp | grep :9000
tcp   LISTEN 0      511        127.0.0.1:9000      0.0.0.0:*    users:(("php-fpm",pid=10,fd=10),("php-fpm",pid=9,fd=10),("php-fpm",pid=8,fd=10),("php-fpm",pid=7,fd=10),("php-fpm",pid=6,fd=10),("php-fpm",pid=1,fd=8))
bash-5.1$ exit
exit
[root@web01 ~]#

4.容器编排工具Docker compose(单机版用docker compose)

Compose概述

-Compose项目是Docker官方的开源项目,负责实现容器集群的快速编排,在Compose中有两个核心概念,分别是服务和项目

-服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。

一项目(project):由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yaml文件中定义

02_私有仓库与dockerfile23.png

4.1 Docker compose项目管理常用命令

说明up创建项目并启动容器down删除项目容器及网络ls列出可以管理的项目start/stop/restart启动项目/停止项目/重启项目images列出项目使用的镜像ps显示项目中容器的状态logs查看下项目中容器的日志

4.2 项目创建

# 检查软件包是否安装
[root@web01 ~]# dnf -y install docker-compose-plugin
上次元数据过期检查:0:43:55 前,执行于 2026年06月01日 星期一 15时26分51秒。
软件包 docker-compose-plugin-5.1.4-1.el9.x86_64 已安装。
依赖关系解决。
无需任何处理。
完毕!

[root@docker ~]# vim docker-compose.yaml
name: websvc                    #项目名称
services:                       #关键字、定义服务
  websvc:                       #定义服务名称 
    container_name: nginx       #定义容器的名称
    image: nginx:latest         #定义容器使用的镜像         

4.4 项目管理

#创建并启动项目
[root@web01 ~]# docker compose -f docker-compose.yaml up -d
 ✔ Network websvc_default Created                                                              0.0s
 ✔ Container nginx        Started  
 
 #查看项目                                                            0.2s
[root@web01 ~]# docker compose ls
NAME                STATUS              CONFIG FILES
websvc              running(1)          /root/docker-compose.yaml

#查看项目中的容器状态
[root@web01 ~]# docker compose -p websvc ps
NAME      IMAGE          COMMAND                   SERVICE   CREATED              STATUS              PORTS
nginx     nginx:latest   "/docker-entrypoint.…"   websvc    About a minute ago   Up About a minute   80/tcp

# 启动、停止、重启项目
[root@web01 ~]# docker compose -p websvc stop
[+] stop 1/1
 ✔ Container nginx Stopped                                                                     0.1s
[root@web01 ~]# docker compose -p websvc start
[+] start 1/1
 ✔ Container nginx Started                                                                     0.1s
[root@web01 ~]# docker compose -p websvc restart
[+] restart 0/1
 ⠹ Container nginx Restarting                                                                  0.2s

# 查看项目中容器的日志
[root@web01 ~]# docker inspect nginx | grep -i address
                    "IPAddress": "172.19.0.2",
                    "MacAddress": "66:6d:4c:3d:24:c9",
                    "GlobalIPv6Address": "",
[root@web01 ~]# curl 172.19.0.2/info.php
.... ....
nginx  | 2026/06/01 08:16:50 [error] 22#22: *1 open() "/usr/share/nginx/html/info.php" failed (2: No such file or directory), client: 172.19.0.1, server: localhost, request: "GET /info.php HTTP/1.1", host: "172.19.0.2"
nginx  | 172.19.0.1 - - [01/Jun/2026:08:16:50 +0000] "GET /info.php HTTP/1.1" 404 153 "-" "curl/7.76.1" "-"

# 查看项目日志
[root@web01 ~]# docker compose -p websvc logs | tail -5f
nginx  | 2026/06/01 08:21:48 [notice] 1#1: start worker process 30
nginx  | 2026/06/01 08:21:48 [notice] 1#1: start worker process 31
nginx  | 2026/06/01 08:21:48 [notice] 1#1: start worker process 32
nginx  | 2026/06/01 08:21:55 [error] 29#29: *1 open() "/usr/share/nginx/html/info.php" failed (2: No such file or directory), client: 172.19.0.1, server: localhost, request: "GET /info.php HTTP/1.1", host: "172.19.0.2"
nginx  | 172.19.0.1 - - [01/Jun/2026:08:21:55 +0000] "GET /info.php HTTP/1.1" 404 153 "-" "curl/7.76.1" "-"


# 删除项目
[root@web01 ~]# docker compose -p websvc down
[+] down 2/2
 ✔ Container nginx        Removed                                                              0.1s
 ✔ Network websvc_default Removed          

4.5 compose常用语法

注意:yaml文件名称

最推荐:不改名,保持默认 **docker-compose.yaml**最简单、最省心、最不容易出错。

不改名 → 直接用 docker compose up -d

改了名 → 必须用 docker compose -f 文件名 up -d

指令说明networks配置容器连接的网络container_name指定容器名称depends_on解决容器的依赖、启动先后的问题command覆盖容器启动后默认执行的命令environment设置环境变量image指定为镜像名称或镜像 IDnetwork_mode设置网络模式restart容器保护策略[always、no、on-failure]ports暴露端口信息volumes数据卷,支持 [volume、bind、tmpfs、npipe]

4.6 容器服务编排:nginx+php

02_私有仓库与dockerfile24.png

02_私有仓库与dockerfile25.png

[root@web01 ~]# docker load -i myos.tar.xz  #在资料里面导入这个镜像,里面包含了myos:nginx myos:php-fpm
[root@web01 ~]# mkdir /var/webroot
[root@web01 ~]# mkdir /var/webconf
[root@web01 ~]# vim /var/webroot/index.php  #手动编写保存
<?php
$i=33;
echo $i;
?>
[root@web01 ~]# ls /var/webconf    #去资料直接下载
nginx.conf

[root@web01 ~]# vim docker-compose.yaml 
name: websvc
services:
  nginxsvc:
    container_name: nginx
    image: myos:nginx
    restart: always
    volumes:
      - type: bind
        source: /var/webconf/nginx.conf
        target: /usr/local/nginx/conf/nginx.conf
      - type: bind
        source: /var/webroot
        target: /usr/local/nginx/html
    network_mode: bridge
    ports:
      - 80:80
    environment:
      - "TZ=Asia/Shanghai"
  php-fpm:
    container_name: php-fpm
    image: myos:php-fpm
    restart: always
    volumes:
      - type: bind
        source: /var/webroot
        target: /usr/local/nginx/html
    depends_on:
      - nginxsvc
    network_mode: "container:nginx"

4.7 验证项目

# 创建,并启动项目
[root@web01 ~]# docker compose -f docker-compose.yaml up -d
[+] Running 2/2
 ⠿ Container nginx    Started             0.3s
 ⠿ Container php-fpm  Started             0.3s

# 查看项目
[root@web01 ~]# docker compose ls
NAME           STATUS        CONFIG FILES
websvc         running(2)    /root/docker-compose.yaml

# 查看容器状态,验证服务
[root@web01 ~]# docker compose -p websvc ps 
NAME           COMMAND                    SERVICE    STATUS
nginx          "nginx -g 'daemon of..."   nginx      running    ......
php-fpm        "php-fpm --nodaemoni..."   php-fpm    running    ......

# 访问 php 页面验证
[root@web01 php+nginx]# curl 172.17.0.2/index.php
33
[root@web01 php+nginx]# curl 127.0.0.1/index.php
33

5.docker compose安装Harbor镜像仓库

harbor述

-Habor 是在Registry 上进行了相应的企业级扩展,从而获得了更加广泛的应用,这些新的企业级特性包括:提供WEB界面,优化用户体验,支持密码登陆、搜索功能,区分公有、私有镜像,以及基于角色的访问控制,集成日志审计、支持水平扩展等功能。

-部署harbor环境非常繁琐,涉及到web服务器、数据库服务器、程序代码、docker 私有镜像仓库等9个应用,所以我们采用部署微服务的方式使用docker-compose的方式部署harbor仓库

02_私有仓库与dockerfile26.png

组件作用通信端口Nginx反向代理,处理 HTTP/HTTPS 请求80, 443Core核心服务(API/UI)8080Registry存储和分发镜像5000PostgreSQL存储用户、项目等元数据5432Redis缓存会话和数据6379JobService执行复制、扫描等异步任务-Trivy镜像漏洞扫描(可选)

5.1 Harbor安装

前提说明

  • 没有证书也能正常安装(使用 HTTP 模式,不配置 HTTPS)
  • 步骤更规范、注释更清晰
  • 路径统一、命令可直接复制
  • 自带自签名证书创建方法(以后想用 HTTPS 也能一键生成)

5.2 修改主机名

[root@web01 ~]# hostnamectl set-hostname harbor
[root@web01 ~]# bash
[root@harbor ~]# vim /etc/hosts
[root@harbor ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.88.101 harbor

[root@harbor ~]# cat /etc/docker/daemon.json
{
.... .....
    "insecure-registries": ["192.168.88.101:443","192.168.88.101:5000"],
 }

[root@harbor ~]# systemctl daemon-reload
[root@harbor ~]# systemctl restart docker.service

5.3上传文件到目录或者下载

cd /usr/local
wget https://github.com/goharbor/harbor/releases/download/v2.12.2/harbor-offline-installer-v2.12.2.tgz

# 解压
tar xvf harbor-offline-installer-v2.12.2.tgz -C /usr/local
cd /usr/local/harbor

5.4 创建证书文件

以下的证书创建是非交互的:提前写好了证书的信息、直接创建

【了解】证书交互模式的创建

[root@harbor ~]# cd /usr/local/harbor
[root@harbor harbor]# docker load -i harbor.v2.7.0.tar.gz
# 创建 https 证书
[root@harbor harbor]# mkdir tls
[root@harbor harbor]# cd tls
[root@harbor tls]# vim san.cnf
[ req ]
default_bits       = 2048
prompt             = no
default_md         = sha256
distinguished_name = dn
req_extensions     = req_ext

[ dn ]
C  = CN
ST = BJ
L  = BJ
O  = edu
OU = student
CN = harbor

[ req_ext ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = harbor
DNS.2 = harbor:443
IP.1  = 127.0.0.1
IP.2  = 192.168.88.101   # 改成你的 harbor 服务器真实IP


[root@harbor harbor]# cd ..
[root@harbor harbor]# openssl genrsa -out tls/cert.key 2048
[root@harbor harbor]# openssl req -new -x509 -days 3650 \
  -key tls/cert.key \
  -out tls/cert.crt \
  -config tls/san.cnf
  
[root@harbor harbor]# openssl x509 -in tls/cert.crt -text -noout | grep -A1 "Subject Alternative Name"
#看到如下输出为正常:
            X509v3 Subject Alternative Name:
                DNS:harbor, DNS:harbor:443, IP Address:127.0.0.1, IP Address:192.168.88.101

5.5 复制并修改配置文件

我们使用主流的https的加密访问

cp harbor.yml.tmpl harbor.yml
vim harbor.yml

[root@harbor harbor]# cp harbor.yml.tmpl harbor.yml
[root@harbor harbor]# vim harbor.yml
05:    hostname: harbor
08:    # http:
10:      # port: 80
17:    certificate: /usr/local/harbor/tls/cert.crt
18:    private_key: /usr/local/harbor/tls/cert.key
47:    harbor_admin_password: admin123

#预安装检查
[root@harbor harbor]# ./prepare

[root@harbor harbor]# docker compose -f docker-compose.yml up -d
 ... ....
                                            
[root@harbor harbor]# echo 'docker compose -f docker-compose.yml up -d' >>/etc/rc.local
[root@harbor harbor]# chmod +x /etc/rc.local


#启动后的状态检查都是healthy为0k
[root@harbor harbor]# docker compose ls
NAME                STATUS              CONFIG FILES
harbor              running(9)          /data/harbor/docker-compose.yml
[root@harbor harbor]# docker compose -p harbor ps

真机浏览器访问harbor仓库 如果有提示、点击高级===》继续前往

02_私有仓库与dockerfile27.png

02_私有仓库与dockerfile28.png

02_私有仓库与dockerfile29.png

创建用户以普通用户的身份登陆harbor仓库是企业中的通常做法

02_私有仓库与dockerfile30.png

02_私有仓库与dockerfile31.png

02_私有仓库与dockerfile32.png

管理员创建项目、将普通用户设置为项目管理员、以普通用户登录harbor仓库推送镜像

02_私有仓库与dockerfile33.png

02_私有仓库与dockerfile34.png

02_私有仓库与dockerfile35.png

管理员退出登录以普通用户登录检查

02_私有仓库与dockerfile36.png

02_私有仓库与dockerfile37.png

5.6 Linux登录到harbor仓库

[root@harbor harbor]# docker login harbor:443
Username: luckydog
Password:

WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded


[root@harbor harbor]# docker tag myos:nginx harbor:443/public/mynginxv2:v2
[root@harbor harbor]# docker push harbor:443/public/mynginxv2:v2
The push refers to repository [harbor:443/public/mynginxv2]
bbd48077e0f3: Pushed
65dbea0a4b39: Pushed
c825984bad08: Pushed
v2: digest: sha256:196194ceb7986666ef2c4c0170122d08a10885e6c28f4b9313bfa771637714c8 size: 745

[root@harbor harbor]# docker push harbor:443/public/my1nginx:v1
The push refers to repository [harbor:443/public/my1nginx]
c825984bad08: Pushed
bbd48077e0f3: Pushed
65dbea0a4b39: Pushed
v1: digest: sha256:196194ceb7986666ef2c4c0170122d08a10885e6c28f4b9313bfa771637714c8 size: 745

到图形界面对应的项目中验证推送过去的镜像,如果推送错了镜像或者要重新推送,可以删除镜像,重新打标签推送


拉取镜像
[root@harbor harbor]# docker rmi harbor:443/public/my1nginx:v1
[root@harbor harbor]# docker pull  harbor:443/public/my1nginx:v1

web页面查看验证

02_私有仓库与dockerfile38.png

【常见问题】SAN证书的问题

[root@harbor harbor]# docker login  harbor:443
Username: admin
Password:
Error response from daemon: Get "https://harbor:443/v2/": tls: 
failed to verify certificate: x509: certificate relies on legacy Common Name field, 
use SANs instead


1.配置域名解析
[root@harbor harbor]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.88.101  harbor

2.修改/etc/docker/daemon.json文件  使用域名替代IP
[root@harbor harbor]# cat /etc/docker/daemon.json
{
  "registry-mirrors":[
    "https://docker.1panelproxy.com",
    "https://hub.openeeds.com",
    "https://dockerproxy.1panel.live",
    "https://docker.1panel.live",
    "https://proxy.1panel.live",
    "https://docker.m.daocloud.io",
    "https://noohub.ru",
    "https://huecker.io",
    "https://dockerhub.timeweb.cloud",
    "https://0c105db5188026850f80c001def654a0.mirror.swr.myhuaweicloud.com",
    "https://5tqw56kt.mirror.aliyuncs.com",
    "https://docker.1panel.live",
    "http://mirrors.ustc.edu.cn/",
    "http://mirror.azure.cn/",
    "https://hub.rat.dev/",
    "https://docker.ckyl.me/",
    "https://docker.chenby.cn",
    "https://docker.hpcloud.cloud",
    "https://docker.m.daocloud.io"
   ],
   "insecure-registries": ["harbor:443","192.168.88.101:5000"]
 }

3.停止docker容器

[root@harbor harbor]# docker compose down
[root@harbor harbor]# systemctl daemon-reload
[root@harbor harbor]# systemctl restart docker.service

4.登录验证、可以登录
[root@harbor harbor]# docker login harbor:443
Username: admin
Password:

WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.  #这是证书保存的位置
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded


再参照上面的打标签推送镜像的步骤就可以了

5.7 使用http访问harbor(可选方案作为了解)

第一步:修改 harbor.yml
找到配置文件:
vim /usr/local/harbor/harbor.yml
注释掉 HTTPS
把这一段:
https:
  port: 443
  certificate: /usr/local/harbor/tls/cert.crt
  private_key: /usr/local/harbor/tls/cert.key
改成:
#https:
#  port: 443
#  certificate: /usr/local/harbor/tls/cert.crt
#  private_key: /usr/local/harbor/tls/cert.key


第二步:确认 HTTP 端口
确保有:
hostname: 192.168.88.101

http:
  port: 80
如果 http 没写 port=80,就补上


第三步:重新生成 Harbor 配置
cd /usr/local/harbor
./prepare


第四步:重启 Harbor
docker compose down
docker compose up -d


第五步:确认 80 端口
ss -lntp | grep 80
应该看到:
0.0.0.0:80


第六步:Docker 登录(HTTP关键点)
Docker 默认禁止 HTTP registry,需要配置 daemon
编辑:
vim /etc/docker/daemon.json
加入:
[root@harbor harbor]# cat /etc/docker/daemon.json
{
  ... .....
    "insecure-registries": ["192.168.88.101:80","192.168.88.101:5000"],
    "registry-mirrors": ["http://harbor:80", "http://registry:5000"],
    "hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]
 }

重启 Docker:
systemctl restart docker


第七步:登录 Harbor(HTTP)
docker login 192.168.88.101:80

[root@harbor harbor]# docker info | grep -A5 "Insecure Registries"
... ...
 Insecure Registries:
  192.168.88.101:5000
  192.168.88.101:80
  ::1/128
  127.0.0.0/8
 Registry Mirrors:
 
#命令行登录测试
[root@harbor harbor]# docker login 192.168.88.101:80
Username: admin
Password:

WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

浏览器可以看到是http的方式登录的

02_私有仓库与dockerfile39.png

6.【了解】需要了解的内容

Alpine 镜像

Alpine(音,艾尔派因)它是一个独立的 Linux 发行版。、

CentOS、Ubuntu、Debian 是“完整版 Linux”。

Alpine 是“极简版 Linux”。

它只有几 MB 大小,
专门为了 Docker 和容器设计。

包管理器是 apk,
不是 yum、dnf、apt。

很多官方镜像(nginx、redis、busybox 等)
都会提供 alpine 版本,
目的是减少镜像体积、加快下载速度。

官方地址

https://www.alpinelinux.org/

02_私有仓库与dockerfile40.png

02_私有仓库与dockerfile41.png

Alpine(音,艾尔派因) 词源于拉丁语 “Alpes”(阿尔卑斯山),原指 “阿尔卑斯山的”。

Alpine Linux 取名也呼应其 “轻量如高山积雪般纯净、精简” 的设计理念。

相关特性

特性AlpineUbuntu/Debian启动速度0.3秒1.5秒内存占用<10MB>50MBShell支持ash (BusyBox)bash调试工具需额外安装默认更丰富生产采用率容器场景>60%通用场景>70%

案例说明

# 基于 Alpine 3.18 构建
FROM alpine:3.18

# 更新包索引并安装必要工具(如 curl)
RUN apk update && apk add --no-cache curl

# 运行命令
CMD ["curl", "https://example.com"]