Linux——Dockerfile部分参数(1)

news2024/11/15 4:55:33

在这里我们来整理一下docker容器、dockerfile、docker镜像的关系:

dockerfile是面向开发的,发布项目做镜像的时候就要编写dockerfile文件。
dockerfile:构建文件,定义了一切的步骤,源代码。
dockerImanges:通过dockerfile构建生成的镜像,最终发布和运行的产品。
docker容器:容器就是镜像运行起来提供服务的

一、Dockerfile 指令选项。

Dockerfile 指令选项:

FROM                  #基础镜像 。 (centos)
MAINTAINER            #镜像的作者和邮箱。(已被弃用,结尾介绍代替词)
RUN                   #镜像构建的时候需要执行的命令。
CMD                   #类似于 RUN 指令,用于运行程序(只有最后一个会生效,可被替代)
EXPOSE                #对外开放的端口。
ENV                   #设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
ADD                   # 步骤:tomcat镜像,这个tomcat压缩包。添加内容。
COPY                  #复制指令,将文件拷贝到镜像中。
VOLUME                #设置卷,挂载的主机目录。
USER                  #用于指定执行后续命令的用户和用户组,
                       这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。
WORKDIR               #工作目录(类似CD命令)。
ENTRYPOINT            #类似于 CMD 指令,但其不会被 docker run 
                       的命令行参数指定的指令所覆盖,会追加命令。
ONBUILD               #当构建一个被继承Dokcerfile,就会运行ONBUILD的指令。出发执行。


注意:CMD类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
CMD 在docker run 时运行。
RUN 是在 docker build。
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。
CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

LABEL(MAINTALNER已经被弃用了,目前是使用LABEL代替)
LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
比如我们可以添加镜像的作者:
LABEL org.opencontainers.image.authors="runoob"

ENV 设置环境变量:

格式有两种:

    ENV <key> <value>

ENV <key1>=<value1> <key2>=<value2>...

这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。

ARG 构建参数:

格式:ARG <参数名>[=<默认值>]

构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。

EXPOSE 声明端口:

格式为 EXPOSE <端口1> [<端口2>...]。

EXPOSE 指令是声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。

WORKDIR 指定工作目录:

格式为 WORKDIR <工作目录路径>。

使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录

[root@docker1 ~]# mkdir /test-multi
[root@docker1 ~]# cd /test-multi
[root@docker1 test-multi]# vim Dockerfile
[root@docker1 test-multi]# cat Dockerfile
FROM centos
ENV aa=1  bb=2  cc=3 envdir=/a/b/c
ARG dd=4  ee=5  ff=6 argdir=/a/b/c
WORKDIR $argdir
COPY test.sh $envdir
RUN echo $aa $bb $cc $dd $dd $ff > /var.txt
CMD ["./test.sh"]
[root@docker1 test-multi]#
[root@docker1 test-multi]# vim test.sh
[root@docker1 test-multi]# cat test.sh
#! /bin/bash
echo output env vars:
echo $aa $bb $cc
echo output arg vars:
echo $dd $ee $ff
echo current work directory:
pwd
[root@docker1 test-multi]#
[root@docker1 test-multi]# chmod +x test.sh



[root@docker1 test-multi]# docker build -t mymulti:v1 .
……
=> [2/4] WORKDIR /a/b/c                                                              0.1s
 => [3/4] COPY test.sh /a/b/c                                                         0.0s
 => [4/4] RUN echo 1 2 3 4 4 6 > /var.txt   
……

验证:
[root@docker1 test-multi]# docker run --rm mymulti:v1
output env vars:
1 2 3
output arg vars:

current work directory:
/a/b/c
[root@docker1 test-multi]#

VOLUME 定义匿名卷:

格式为:

    VOLUME ["<路径1>", "<路径2>"...]

    VOLUME <路径>

之前我们说过,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,后面的章节我们会进一步介绍 Docker 卷的概念。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。

[root@docker1 ~]# mkdir /test_volume
[root@docker1 ~]# cd /test_volume
[root@docker1 test_volume]#
[root@docker1 test_volume]# cat Dockerfile
from alpine
RUN mkdir /test && touch /test/test.txt
[root@docker1 test_volume]# docker build -t myvolume:v1 .
[root@docker1 test_volume]# docker run -it myvolume:v1 sh
/ # cat /test/test.txt 
/ # echo 123 > /test/test.txt 
/ # cat /test/test.txt 
123
/ # exit
[root@docker1 test_volume]# docker ps -a
CONTAINER ID   IMAGE         COMMAND   CREATED          STATUS                     PORTS     NAMES
c2de419ef2d3   myvolume:v1   "sh"      36 seconds ago   Exited (0) 3 seconds ago             zealous_lumiere
[root@docker1 test_volume]#
[root@docker1 test_volume]# docker start zealous_lumiere 
zealous_lumiere
[root@docker1 test_volume]# docker ps -a
CONTAINER ID   IMAGE         COMMAND   CREATED              STATUS         PORTS     NAMES
c2de419ef2d3   myvolume:v1   "sh"      About a minute ago   Up 7 seconds             zealous_lumiere
[root@docker1 test_volume]# docker exec -it zealous_lumiere sh
/ # cat /test/test.txt 
123
/ # exit
[root@docker1 test_volume]# docker stop zealous_lumiere 
zealous_lumiere
[root@docker1 test_volume]# docker rm zealous_lumiere 
zealous_lumiere
[root@docker1 test_volume]#注:容器没删除,则数据存在

重新创建的容器没有之前的数据,数据是非持久化的:
[root@docker1 test_volume]# docker run -it --name mv1  myvolume:v1 sh
/ # cat /test/test.txt 
/ # exit
[root@docker1 test_volume]# docker rm mv1 
mv1
[root@docker1 test_volume]#

匿名卷:

[root@docker1 test_volume]# cat Dockerfile

from alpine

RUN mkdir /test && touch /test/test.txt

VOLUME /test

[root@docker1 test_volume]#

[root@docker1 test_volume]# docker build -t myvolume:v2 .

[root@docker1 test_volume]# docker run --rm -d myvolume:v2 sleep 100

2b2922070b503298b4195c7fa7007a6b0f3f51528f035098beb4d32801288c11

[root@docker1 test_volume]#

[root@docker1 test_volume]# docker run --rm -d myvolume:v2 sleep 100

2b2922070b503298b4195c7fa7007a6b0f3f51528f035098beb4d32801288c11

[root@docker1 test_volume]# docker ps -a

CONTAINER ID   IMAGE         COMMAND       CREATED          STATUS          PORTS     NAMES

2b2922070b50   myvolume:v2   "sleep 100"   13 seconds ago   Up 13 seconds             eloquent_agnesi

[root@docker1 test_volume]# docker inspect eloquent_agnesi | grep -i -A 5 mounts

        "Mounts": [

            {

                "Type": "volume",

                "Name": "581af512e8399049c748fb8f2918df6041435aba355b83618537accbada60f72",

                "Source": "/var/lib/docker/volumes/581af512e8399049c748fb8f2918df6041435aba355b83618537accbada60f72/_data",

                "Destination": "/test",

[root@docker1 test_volume]# docker volume ls

DRIVER    VOLUME NAME

local     581af512e8399049c748fb8f2918df6041435aba355b83618537accbada60f72

[root@docker1 test_volume]#

[root@docker1 test_volume]# docker ps -a(容器停止后自动删除了)

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

[root@docker1 test_volume]# docker volume ls 匿名卷生命周期跟随容器

DRIVER    VOLUME NAME

[root@docker1 test_volume]#

不在dockerfile定义volume也可以启用匿名卷:

[root@docker1 test_volume]# docker run -d -v /opt alpine sleep 200

37d9216c975dac39d75845bfa14db0b983d8eb710d1072ce75d16b5d609d54db

[root@docker1 test_volume]# docker volume ls

DRIVER    VOLUME NAME

local     847f7e6cc35beca7f50a9a6b41c28a84b7d60abfabd3e7677636f84f5d8694a4

[root@docker1 test_volume]#

[root@docker1 test_volume]# docker rmi myvolume:v2

Untagged: myvolume:v2

Deleted: sha256:6ccc8e360eed876906d7bf5fdd8109a995e7afe11b8595f00ec594db81760664

[root@docker1 test_volume]#

持久存储:推荐使用命名卷或者宿主机目录(或共享的目录)

数据卷管理:数据持久化

这一章介绍如何在 Docker 内部以及容器之间管理数据,在容器中管理数据主要有两种方式:

    数据卷(Volumes)

挂载主机目录 (Bind mounts)

[root@docker1 test_volume]# docker volume ls
DRIVER    VOLUME NAME
[root@docker1 test_volume]#
[root@docker1 test_volume]# docker volume create my-vol1
my-vol1
[root@docker1 test_volume]# docker volume ls
DRIVER    VOLUME NAME
local     my-vol1
[root@docker1 test_volume]#
[root@docker1 test_volume]# docker inspect my-vol1 
[
    {
        "CreatedAt": "2024-05-21T09:32:49+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/my-vol1/_data",
        "Name": "my-vol1",
        "Options": null,
        "Scope": "local"
    }
]
[root@docker1 test_volume]#

[root@docker1 test_volume]# cat Dockerfile 
from alpine
RUN mkdir /test && touch /test/test.txt
VOLUME /test
[root@docker1 test_volume]#
[root@docker1 test_volume]# docker build -t myvolume:v2 .
[root@docker1 test_volume]# docker inspect myvolume:v2| grep -i -A 2  volumes
            "Volumes": {
                "/test": {}
            },
[root@docker1 test_volume]#

[root@docker1 test_volume]# docker run -it --rm  -v my-vol1:/test myvolume:v2 sh
/ # cat /test/test.txt 
/ # echo 123 > /test/test.txt 
/ # echo 456 > /test/test2.txt
/ # exit
[root@docker1 test_volume]# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
[root@docker1 test_volume]# docker run -it --rm  -v my-vol1:/test myvolume:v2 sh
/ # cat /test/test.txt 
123
/ # cat /test/test2.txt 
456
/ # exit
[root@docker1 test_volume]#
[root@docker1 ~]# ls /var/lib/docker/volumes/my-vol1/_data
test2.txt  test.txt
[root@docker1 ~]#

[root@docker1 test_volume]# docker run -it --rm  --mount source=my-vol1,target=/test myvolume:v2 sh
/ # cat /test/test2.txt 
456
/ # exit
[root@docker1 test_volume]#

Linux目录挂载:加bind选项
[root@docker1 ~]# mkdir /a /b
[root@docker1 ~]# touch  /b/b.txt
[root@docker1 ~]# mount /a /b
mount: /b: /a is not a block device.
[root@docker1 ~]# mount -o bind /a /b
[root@docker1 ~]# ls /b
[root@docker1 ~]# umount /b
[root@docker1 ~]# ls /b
b.txt
[root@docker1 ~]#

[root@docker1 test_volume]# mkdir /testmount
[root@docker1 test_volume]# echo 3 > /testmount/3.txt
[root@docker1 test_volume]# docker run -it --rm  --mount type=bind,source=/testmount,target=/test myvolume:v2 sh
/ # ls /test
3.txt
/ # echo 33 > /test/3.txt 
/ # exit
[root@docker1 test_volume]# docker run -it --rm  --mount type=bind,source=/testmount,target=/test,readonly myvolume:v2 sh
/ # cat /test/3.txt 
33
/ # echo 333 > /test/3.txt 
sh: can't create /test/3.txt: Read-only file system
/ # exit
[root@docker1 test_volume]#

容器共享存储:

[root@docker1 ~]# ls /webdir/
Dockerfile  page1.html  page2.html
[root@docker1 ~]# cat /webdir/page1.html 
page1
[root@docker1 ~]# cat /webdir/page2.html 
page2
[root@docker1 ~]#
[root@docker1 test_volume]# docker run -d --rm --name nginx1 -v /webdir:/usr/share/nginx/html -p 81:80 nginx:latest 
6b9920b4b7d3f666c486a1d372b45fa01b63cd64252fbdc629053aff0b953ce8
[root@docker1 test_volume]# docker run -d --rm --name nginx2 -v /webdir:/usr/share/nginx/html -p 82:80 nginx:latest 
9666d000921ee7d78edaf8fc0f91ab4127b68ca9195c70601fa3eaf628c79616
[root@docker1 test_volume]# curl 127.0.0.1:81/page1.html
page1
[root@docker1 test_volume]# curl 127.0.0.1:82/page1.html
page1
[root@docker1 test_volume]# docker stop nginx1
nginx1
[root@docker1 test_volume]# docker stop nginx2
nginx2
[root@docker1 test_volume]#

使用local以外的volume driver:

使用vieux/sshfs:

安装插件:

[root@docker1 test_volume]# docker plugin install --grant-all-permissions vieux/sshfs
latest: Pulling from vieux/sshfs
Digest: sha256:1d3c3e42c12138da5ef7873b97f7f32cf99fb6edde75fa4f0bcf9ed277855811
52d435ada6a4: Complete 
Installed plugin vieux/sshfs
[root@docker1 test_volume]#

[root@docker2 ~]# mkdir /sharedir
[root@docker2 ~]# touch /sharedir/share.txt

创建卷:

[root@docker1 test_volume]# docker volume create --driver vieux/sshfs -o sshcmd=root@192.168.99.129:/sharedir -o password=1 sshvolume
sshvolume
[root@docker1 test_volume]#
[root@docker1 test_volume]# docker volume ls
DRIVER               VOLUME NAME
local                my-vol1
vieux/sshfs:latest   sshvolume
[root@docker1 test_volume]#
[root@docker1 test_volume]# docker run -it --rm -v sshvolume:/opt alpine sh
/ # ls /opt/
share.txt
/ # exit
[root@docker1 test_volume]#
(
官方参考如下:
docker run -d \
  --name sshfs-container \
  --volume-driver vieux/sshfs \
  --mount src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword \
  nginx:latest
)
使用基于nfs的卷:
(
官方参考如下:
Nfsv3:
docker service create -d \
  --name nfs-service \
  --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,volume-opt=o=addr=10.0.0.10' \
  nginx:latest

nfsv4:
docker service create -d \
    --name nfs-service \
    --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,"volume-opt=o=addr=10.0.0.10,rw,nfsvers=4,async"' \
    nginx:latest
)
配置nfs服务,自行解决防火墙和selinux:
[root@docker2 ~]# mkdir /nfs1 /nfs2
[root@docker2 ~]# chmod 777  /nfs1 /nfs2
[root@docker2 ~]# vim /etc/exports
[root@docker2 ~]# cat /etc/exports
/nfs1  *(rw,no_root_squash)
/nfs2 *(rw,no_root_squash)
[root@docker2 ~]# 
[root@docker2 ~]# systemctl start nfs-server.service

[root@docker1 test_volume]# yum -y install nfs-utils
[root@docker1 test_volume]# showmount -e 192.168.99.129
Export list for 192.168.99.129:
/nfs2 *
/nfs1 *
[root@docker1 test_volume]#

手动创建卷:

[root@docker1 test_volume]# docker volume create --driver local -o type=nfs -o device=:/nfs1 -o o=addr=192.168.99.129,rw,nfsvers=4  nfs1volume
nfs1volume
[root@docker1 test_volume]#
[root@docker1 test_volume]# docker volume ls
DRIVER               VOLUME NAME
local                my-vol1
local                nfs1volume
vieux/sshfs:latest   sshvolume
[root@docker1 test_volume]#
验证:
[root@docker1 test_volume]# docker run -it --rm --mount 'type=volume,source=nfs1volume,target=/opt,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/nfs1,"volume-opt=o=addr=192.168.99.129,rw,nfsvers=4"' --privileged alpine sh
/ # touch /opt/1.txt
/ # exit
[root@docker1 test_volume]#
[root@docker2 ~]# ls /nfs1
1.txt
[root@docker2 ~]#
[root@docker1 test_volume]# docker run -it --rm --mount 'type=volume,source=nfs1volume,target=/opt' --privileged alpine sh
/ # ls /opt/
1.txt
/ # exit
[root@docker1 test_volume]#

创建容器时自动创建nfs卷:

[root@docker1 ~]# docker volume ls
DRIVER               VOLUME NAME
local                my-vol1
local                nfs1volume
vieux/sshfs:latest   sshvolume
[root@docker1 ~]#
[root@docker1 test_volume]# docker run -it --rm --mount 'type=volume,source=nfs2volume,target=/opt,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/nfs2,"volume-opt=o=addr=192.168.99.129,rw,nfsvers=4"' --privileged alpine sh
/ # touch /opt/2.txt
/ # exit
[root@docker1 test_volume]# 
[root@docker1 test_volume]# docker volume ls
DRIVER               VOLUME NAME
local                my-vol1
local                nfs1volume
local                nfs2volume
vieux/sshfs:latest   sshvolume
[root@docker1 test_volume]#
[root@docker2 ~]# ls /nfs2
2.txt
[root@docker2 ~]#

练习:使用samba共享提供持久存储

官方参考:

docker volume create \

--driver local \

--opt type=cifs \

--opt device=//uxxxxx.your-server.de/backup \

--opt o=addr=uxxxxx.your-server.de,username=uxxxxxxx,password=*****,file_mode=0777,dir_mode=0777 \

--name cif-volume

其他数据卷内容参考:Volumes | Docker DocsLearn how to create, manage, and use volumes instead of bind mounts for persisting data generated and used by Docker.icon-default.png?t=N7T8https://docs.docker.com/storage/volumes/

USER 指定当前用户:

格式:USER <用户名>[:<用户组>]

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。

USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。

[root@docker1 test_volume]# vim Dockerfile 
[root@docker1 test_volume]# cat Dockerfile
from centos
RUN id > /user.txt && chmod 777 /user.txt
RUN useradd test 
USER test
RUN  id >> /user.txt
CMD cat /user.txt
[root@docker1 test_volume]#
[root@docker1 test_volume]# docker build -t test-user:v1 .
[root@docker1 test_volume]# docker run -it --rm test-user:v1 
uid=0(root) gid=0(root) groups=0(root)
uid=1000(test) gid=1000(test) groups=1000(test)
[root@docker1 test_volume]#

HEALTHCHECK 健康检查:

格式:

 HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令

 HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。

[root@docker1 ~]# mkdir /test-health
[root@docker1 ~]# cd /test-health
[root@docker1 test-health]# vim Dockerfile
[root@docker1 test-health]# cat Dockerfile
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s  CMD curl -fs http://localhost/ || exit 1
[root@docker1 test-health]# docker build -t myhealth:v1 .

另起一个终端,每秒刷新一次docker ps -a的输出:
[root@docker1 ~]# watch -n 1 docker ps -a

[root@docker1 test-health]# docker run -d --rm --name myh myhealth:v1 
a1fffb3d0193408d81a1db9484576a3e2df214b12d9fa19ae78da6ff875d7704
[root@docker1 test-health]#
[root@docker1 ~]# docker ps -a
CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS                    PORTS     NAMES
a1fffb3d0193   myhealth:v1   "/docker-entrypoint.…"   28 seconds ago   Up 27 seconds (healthy)   80/tcp    myh
[root@docker1 ~]# 速度快,可以看到从starting到healthy的过程

为了帮助排障,健康检查命令的输出(包括 stdout 以及 stderr)都会被存储于健康状态里,可以用 docker inspect 来查看。
[root@docker1 test-health]#  docker inspect --format '{{json .State.Health}}' myh | python3 -m json.tool

模拟故障:

再容器中删除index.html,因为健康检查指令curl -fs http://localhost/ || exit 1查看的时首页,此时删除首页则访问报错,但是不能使用nginx -s stop停服务,因为nginx进程是容器的主体进程,停服务则容器退出:

[root@docker1 test-health]# docker exec -it myh sh
# ls /usr/share/nginx/html/index.html
/usr/share/nginx/html/index.html
# rm -rf /usr/share/nginx/html/index.html
# exit
[root@docker1 test-health]#
等待一段时间可以看到unhealthy:
[root@docker1 ~]# docker ps -a
CONTAINER ID   IMAGE         COMMAND                  CREATED         STATUS                     PORTS     NAMES
cf26f8e926d2   myhealth:v1   "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes (unhealthy)   80/tcp    myh
[root@docker1 ~]#
自行创建index.html,测试恢复为healthy的过程

[root@docker1 test-health]# vim Dockerfile 
[root@docker1 test-health]# cat Dockerfile
FROM alpine
LABEL custom alpine

[root@docker1 test-health]# docker build -t mylabel:v1 .
[root@docker1 test-health]# docker image ls -f LABEL=custom
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
mylabel      v1        bcefe279deed   3 months ago   7.38MB
[root@docker1 test-health]#

练习

从centos基础镜像创建一个容器,安装nginx软件包,配置健康检查,如果/usr/share/nginx/html/test.html存在,则检查正常,不存在则unhealthy,验证效果

[root@docker1 ~]# mkdir /test-h2
[root@docker1 ~]# cd /test-h2
[root@docker1 test-h2]# vim Dockerfile
[root@docker1 test-h2]# docker build -t myh2 .
[root@docker1 test-h2]# cat Dockerfile 
FROM centos
RUN rm -rf /etc/yum.repos.d/* \
    && curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo \
    && yum -y install nginx \
    && yum clean all \
    && echo test > /usr/share/nginx/html/test.html
EXPOSE 80
HEALTHCHECK --interval=5s --timeout=3s \
   CMD [ -f "/usr/share/nginx/html/test.html" ] || exit 1
[root@docker1 test-h2]# 
注: 此时运行容器,没有启动nginx服务,无法使用curl判定,所以使用[ -f “xxxx” ]

练习2

使用samba共享提供卷

[root@docker2 ~]# mkdir /sambashare
[root@docker2 ~]# yum -y install samba samba-client
[root@docker2 ~]# vim /etc/samba/smb.conf
[root@docker2 ~]# tail -5 /etc/samba/smb.conf
[smbshare]
        path = /sambashare/
        read only = No
        hosts allow = 192.168.99.
        valid users = testuser
[root@docker2 ~]#
[root@docker2 ~]# chmod 777 /sambashare/
[root@docker2 ~]# useradd -s /sbin/nologin testuser
[root@docker2 ~]# pdbedit -a testuser
new password: (redhat)
retype new password:(redhat)
……
[root@docker2 ~]# pdbedit -L
testuser:1001:
[root@docker2 ~]#
[root@docker2 ~]# systemctl restart smb.service nmb.service
[root@docker1 test-h2]# yum -y install samba-client
使用testuser用户和redhat密码查看192.168.99.129服务器的共享:
[root@docker1 ~]# smbclient -L 192.168.99.129 -U testuser%redhat

	Sharename       Type      Comment
	---------       ----      -------
	print$          Disk      Printer Drivers
	smbshare        Disk      
	IPC$            IPC       IPC Service (Samba 4.19.4)
	testuser        Disk      Home Directories
SMB1 disabled -- no workgroup available
[root@docker1 ~]#
[root@docker1 ~]# smbclient -L 192.168.99.129 (直接回车,使用匿名用户查看)
Password for [SAMBA\root]:
Anonymous login successful

	Sharename       Type      Comment
	---------       ----      -------
	print$          Disk      Printer Drivers
	smbshare        Disk      
	IPC$            IPC       IPC Service (Samba 4.19.4)
SMB1 disabled -- no workgroup available
[root@docker1 ~]#
[root@docker1 ~]# yum -y install cifs-utils
[root@docker1 ~]# mount -o username=testuser,password=redhat //192.168.99.129/smbshare /opt
[root@docker1 ~]# df -h /opt
Filesystem                 Size  Used Avail Use% Mounted on
//192.168.99.129/smbshare   70G   13G   58G  19% /opt
[root@docker1 ~]#
[root@docker1 ~]# umount /opt


创建samba共享的卷:
[root@docker1 ~]# docker volume create \
> --driver local \
> --opt type=cifs \
> --opt device=//192.168.99.129/smbshare \
> --opt o=addr=192.168.99.129,username=testuser,password=redhat,file_mode=0777,dir_mode=0777 \
> --name cifs-volume
cifs-volume
[root@docker1 ~]#

[root@docker1 ~]# docker run -it --rm -v cifs-volume:/opt alpine sh
/ # touch /opt/cifs.txt
/ # exit
[root@docker1 ~]#
[root@docker1 ~]# docker volume ls 
DRIVER    VOLUME NAME
local     cifs-volume
local     my-vol1
[root@docker1 ~]# 
[root@docker2 ~]# ls /sambashare/
cifs.txt
[root@docker2 ~]#
[root@docker1 ~]# docker volume rm cifs-volume 
cifs-volume
[root@docker1 ~]#
[root@docker2 ~]# systemctl stop smb nmb

 SHELL 指令:

[root@docker1 test-shell]# cat Dockerfile 
from centos
RUN rm -rf /etc/yum.repos.d/* \
    && curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo \
    && yum -y install python3 \
    && yum clean all \ 
    && touch /result.txt
COPY * /
SHELL ["/usr/bin/python3"]
RUN  /python.py
SHELL ["/bin/bash", "-c"]
RUN /shell.sh
CMD cat /result.txt
[root@docker1 test-shell]#

[root@docker1 test-shell]# cat shell.sh 
echo hello shell >> /result.txt 
[root@docker1 test-shell]# cat python.py 
import os
with open('/result.txt', 'w') as f:
    f.write("hello python\n")

[root@docker1 test-shell]#

[root@docker1 test-shell]# docker build -t myshell:v1 .
[root@docker1 test-shell]# docker run --rm myshell:v1 
hello python
hello shell
[root@docker1 test-shell]#

ONBUILD 为他人做嫁衣裳:

ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的。

[root@docker1 ~]# mkdir /test-onbuild
[root@docker1 ~]# cd /test-onbuild
[root@docker1 test-onbuild]# cat start.sh 
#! /bin/bash
shell_var=$(ls /*.sh)
$shell_var
[root@docker1 test-onbuild]# chmod +x start.sh
[root@docker1 test-onbuild]# cat Dockerfile 
from centos
RUN mkdir /app
WORKDIR /app
ONBUILD COPY *.txt /
ONBUILD COPY *.sh /
COPY start.sh /app
CMD ["./start.sh"]
[root@docker1 test-onbuild]#
[root@docker1 test-onbuild]# docker build -t onbuild_base .
注:此时ONBUILD指令没有执行

[root@docker1 ob1]# pwd
/ob1
[root@docker1 ob1]#
[root@docker1 ob1]# cat shell.sh 
#! /bin/bash
cat /1.txt > /12.txt
cat /2.txt >> /12.txt
cat /12.txt
[root@docker1 ob1]# 
[root@docker1 ob1]# chmod +x shell.sh
[root@docker1 ob1]# cat 1.txt 
1
[root@docker1 ob1]# cat 2.txt 
2
[root@docker1 ob1]# cat Dockerfile 
FROM onbuild_base:latest
[root@docker1 ob1]#
[root@docker1 ob1]# docker build -t ob_sub:v1 .
[root@docker1 ob1]# docker run --rm ob_sub:v1
1
2
[root@docker1 ob1]#



[root@docker1 ob1]# mkdir /ob2
[root@docker1 ob1]# cd /ob2
[root@docker1 ob2]# vim jiaoben.sh
[root@docker1 ob2]# cat jiaoben.sh 
#! /bin/bash
cat /3.txt > /34.txt
cat /4.txt >> /34.txt
cat /34.txt
[root@docker1 ob2]# 
[root@docker1 ob2]# chmod +x jiaoben.sh
[root@docker1 ob2]# echo 3 > 3.txt
[root@docker1 ob2]# echo 4 > 4.txt
[root@docker1 ob2]#
[root@docker1 ob2]# vim Dockerfile
[root@docker1 ob2]# cat Dockerfile
from onbuild_base:latest
[root@docker1 ob2]#
[root@docker1 ob2]# docker build -t ob_sub:v2 .
[root@docker1 ob2]# docker run --rm ob_sub:v2
3
4
[root@docker1 ob2]#

参考文档:

    1. Dockerfie 官方文档:https://docs.docker.com/engine/reference/builder/
    2. Dockerfile 最佳实践文档:

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

    1. Docker 官方镜像 Dockerfile:https://github.com/docker-library/docsicon-default.png?t=N7T8https://github.com/docker-library/docs

 练习:

  1. 在docker1安装httpd服务,共享页面index.html(内容为common-index)
  2. 制作基础镜像osbase:

以centos为基础,配置yum源,安装wget,拷贝脚本web.sh至/app

脚本内容:

   判断安装的web服务是什么:

       如果是httpd,则执行httpd -D FOREGROUND

       如果是nginx,则执行nginx -g “daemon off;”

在dockerfile中使用onbuild关键字,将docker1共享的index.html下载到/目录

  1. 在osbase的基础上构建镜像,安装httpd或nginx,将/index.html拷贝至网站根目录

镜像分别为os_httpd和os_nginx

在每个镜像中,有对应的ONBUILD指令,将页面文件拷贝至对应服务的网站根目录中

  1. 在os_httpd和os_nginx的基础上,构建镜像,拷贝页面至指定目录,测试访问

多阶段构建:

使用单个dockerfile可能遇到的问题:

将所有的构建过程编包含在一个 Dockerfile 中,包括项目及其依赖库的编译、测试、打包等流程,这里可能会带来的一些问题:

    镜像层次多,镜像体积较大,部署时间变长

源代码存在泄露的风险

使用一个dockerfile源码编译出hello.c的二进制文件并运行:

[root@docker1 ~]# mkdir /multi-hello
[root@docker1 ~]# cd /multi-hello
[root@docker1 multi-hello]#
[root@docker1 multi-hello]# vim hello.c
[root@docker1 multi-hello]# cat hello.c
#include<stdio.h> 
int main(void)
{
  printf ("Hello world!\n");                 
  return 0;
}
[root@docker1 multi-hello]#

[root@docker1 multi-hello]# cat Dockerfile
FROM centos
RUN rm -rf /etc/yum.repos.d/* \
    && curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo \
    && yum -y install gcc gcc-c++ make \
    && yum clean all \
    && mkdir /app
COPY hello.c /
RUN gcc -o /hello /hello.c && mv /hello /app
CMD ["/app/hello"]
[root@docker1 multi-hello]#
[root@docker1 multi-hello]# docker build -t multi-hello:v1 .
[root@docker1 multi-hello]# docker run --rm multi-hello:v1
Hello world!
[root@docker1 multi-hello]#

此时镜像多了200M左右:
[root@docker1 multi-hello]# docker image ls centos
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
centos       latest    5d0da3dc9764   2 years ago   231MB
[root@docker1 multi-hello]# docker image ls multi-hello:v1 
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
multi-hello   v1        42afade795ac   51 seconds ago   424MB
[root@docker1 multi-hello]#


如果想要所需的效果,及运行二进制文件,编译环境不是必须的,所有可以分两次构建:
[root@docker1 multi-hello]# ls
Dockerfile  hello.c
[root@docker1 multi-hello]#

从容器复制文件到宿主机,只需容器存在即可,所有此处用了docker create
(使用docker run也可以)
[root@docker1 multi-hello]# docker create --name m1 multi-hello:v1
14285a08b091a714ae9182a61ab90d75df7437987cd2a702ce84d7cd315487da
[root@docker1 multi-hello]# docker ps -a
CONTAINER ID   IMAGE            COMMAND        CREATED         STATUS    PORTS     NAMES
14285a08b091   multi-hello:v1   "/app/hello"   4 seconds ago   Created             m1
[root@docker1 multi-hello]#

[root@docker1 multi-hello]# docker cp m1:/app/hello .
Successfully copied 19.5kB to /multi-hello/.
[root@docker1 multi-hello]# ls hello
hello
[root@docker1 multi-hello]#
[root@docker1 multi-hello]# vim dockerfile2 
[root@docker1 multi-hello]# cat dockerfile2
FROM centos
RUN  mkdir /app
COPY hello /app
CMD ["/app/hello"]
[root@docker1 multi-hello]#
[root@docker1 multi-hello]# docker build -f dockerfile2 -t multi-hello:v2 .
[root@docker1 multi-hello]# docker run --rm multi-hello:v2
Hello world!
[root@docker1 multi-hello]#

[root@docker1 multi-hello]# docker image ls multi-hello
REPOSITORY    TAG       IMAGE ID       CREATED              SIZE
multi-hello   v2        99036d01a922   About a minute ago   231MB
multi-hello   v1        42afade795ac   10 minutes ago       424MB
[root@docker1 multi-hello]# docker image ls centos
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
centos       latest    5d0da3dc9764   2 years ago   231MB
[root@docker1 multi-hello]#
此时multi-hello:v2实现了同样的效果,但是体积小了很多

多阶段构建即使用1个dockerfile实现上面的多次构建过程:

[root@docker1 multi-hello]# rm -rf hello
[root@docker1 multi-hello]# vim dockerfile3
[root@docker1 multi-hello]# vim dockerfile3
[root@docker1 multi-hello]# cat dockerfile3
FROM centos as build1
RUN rm -rf /etc/yum.repos.d/* \
    && curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo \
    && yum -y install gcc gcc-c++ make \
    && yum clean all \
COPY hello.c /
RUN gcc -o /hello /hello.c

FROM centos as build2
WORKDIR /app
COPY --from=build1 /hello .
CMD ["/app/hello"]
[root@docker1 multi-hello]#
[root@docker1 multi-hello]# docker build -f dockerfile3 -t multi-hello:v3 .
[root@docker1 multi-hello]# docker run --rm multi-hello:v3
Hello world!
[root@docker1 multi-hello]# docker image ls multi-hello
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
multi-hello   v3        0163f8be5f45   18 seconds ago   231MB
multi-hello   v2        99036d01a922   17 minutes ago   231MB
multi-hello   v1        42afade795ac   26 minutes ago   424MB
[root@docker1 multi-hello]#

练习:三阶段的构建

第一阶段构:

下载两个包glibc-static-2.28-251.el8.x86_64.rpm和libxcrypt-static-4.1.1-6.el8.x86_64.rpm

第二阶段:

从第一阶段拷贝两个包到根目录并安装,拷贝hello.c的源码到根目录

使用静态编译,如:gcc -o hello -static hello.c

第三阶段:基于空白镜像

获取第二阶段的hello二进制文件,复制到/app目录,并从cmd运行

[root@docker1 ~]# docker image ls centos_stream8
REPOSITORY       TAG       IMAGE ID       CREATED        SIZE
centos_stream8   latest    e502e2d948d7   18 hours ago   335MB
[root@docker1 ~]# 

[root@docker1 ~]# mkdir /mbuild
[root@docker1 ~]# cd /mbuild
[root@docker1 mbuild]# vim Dockerfile
[root@docker1 mbuild]# cat Dockerfile
from centos_stream8 as build1
RUN yum -y install wget \
    && wget http://rpmfind.net/linux/centos/8-stream/PowerTools/x86_64/os/Packages/glibc-static-2.28-251.el8.x86_64.rpm \
    && wget http://rpmfind.net/linux/centos/8-stream/PowerTools/x86_64/os/Packages/libxcrypt-static-4.1.1-6.el8.x86_64.rpm

from centos_stream8 as build2
COPY --from=build1 /*.rpm .
COPY hello.c  /
RUN  yum -y install gcc /glibc-static-2.28-251.el8.x86_64.rpm /libxcrypt-static-4.1.1-6.el8.x86_64.rpm \
    && gcc -o /hello -static /hello.c

from scratch as build3
WORKDIR /app
COPY --from=build2 /hello /app
CMD ["./hello"]

[root@docker1 mbuild]#
[root@docker1 mbuild]# vim hello.c
[root@docker1 mbuild]# cat hello.c
#include<stdio.h> 
int main(void)
{
  printf ("Hello world!\n");                 
  return 0;
}
[root@docker1 mbuild]#

[root@docker1 mbuild]# docker build -t mhello .
[root@docker1 mbuild]# docker image ls mhello
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
mhello       latest    17b68d3ec2d5   16 seconds ago   1.75MB
[root@docker1 mbuild]# docker run --rm mhello
Hello world!
[root@docker1 mbuild]#

注:可以使用--target,构建其中某一个阶段的镜像
如:docker build --target build1 -t mhello-prepare .

练习:

在docker2部署私有仓库,并配置https

在docker1能够直接将镜像推送到docker2的仓库中即可

[root@docker2 ~]# cat /etc/docker/daemon.json

cat: /etc/docker/daemon.json: No such file or directory

[root@docker2 ~]#

[root@docker2 ~]# docker pull registry

[root@docker2 ~]# docker image ls registry

REPOSITORY   TAG       IMAGE ID       CREATED        SIZE

registry     latest    d6b2c32a0f14   7 months ago   25.4MB

[root@docker2 ~]#

 

这里假设我们将要搭建的私有仓库地址为 docker.domain.com,下面我们介绍使用 openssl 自行签发 docker.domain.com 的站点 SSL 证书。

第一步创建 CA 私钥:

[root@docker2 certs]# openssl genrsa -out "root-ca.key" 4096
[root@docker2 certs]# ls root-ca.key 
root-ca.key
[root@docker2 certs]#

 第二步利用私钥创建 CA 根证书请求文件:

[root@docker2 certs]# openssl req \
>           -new -key "root-ca.key" \
>           -out "root-ca.csr" -sha256 \
>           -subj '/C=CN/ST=tj/L=tj/O=tj/CN=tj Docker Registry CA'
[root@docker2 certs]# ls 
root-ca.csr  root-ca.key
[root@docker2 certs]#

第三步配置 CA 根证书,新建 root-ca.cnf:

[root@docker2 certs]# vim root-ca.cnf
[root@docker2 certs]# cat root-ca.cnf
[root_ca]
basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
subjectKeyIdentifier=hash
[root@docker2 certs]#

第四步签发根证书:

[root@docker2 certs]# openssl x509 -req  -days 3650  -in "root-ca.csr" \
>                -signkey "root-ca.key" -sha256 -out "root-ca.crt" \
>                -extfile "root-ca.cnf" -extensions \
>                root_ca
Signature ok
subject=C = CN, ST = tj, L = tj, O = tj, CN = tj Docker Registry CA
Getting Private key
[root@docker2 certs]# ls
root-ca.cnf  root-ca.crt(根证书)  root-ca.csr(签名请求)  root-ca.key(私钥)
[root@docker2 certs]#

第五步生成站点 SSL 私钥:

[root@docker2 certs]# openssl genrsa -out "docker.domain.com.key" 4096
Generating RSA private key, 4096 bit long modulus (2 primes)
...............................................++++
..................................................................++++
e is 65537 (0x010001)
[root@docker2 certs]#
[root@docker2 certs]# ls
docker.domain.com.key  root-ca.cnf  root-ca.crt  root-ca.csr  root-ca.key
[root@docker2 certs]#

第六步使用私钥生成证书请求文件:

[root@docker2 certs]#  openssl req -new -key "docker.domain.com.key" -out "site.csr" -sha256 \
>           -subj '/C=CN/ST=tj/L=tj/O=tj/CN=docker.domain.com'
[root@docker2 certs]# ls
docker.domain.com.key  root-ca.crt  root-ca.key
root-ca.cnf            root-ca.csr  site.csr
[root@docker2 certs]#

第七步配置证书,新建 site.cnf 文件:

[root@docker2 certs]# vim site.cnf
[root@docker2 certs]# cat site.cnf
[server]
authorityKeyIdentifier=keyid,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage=serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectAltName = DNS:docker.domain.com, IP:127.0.0.1
subjectKeyIdentifier=hash
[root@docker2 certs]#

第八步签署站点 SSL 证书:

[root@docker2 certs]# openssl x509 -req -days 750 -in "site.csr" -sha256 \
>     -CA "root-ca.crt" -CAkey "root-ca.key"  -CAcreateserial \
>     -out "docker.domain.com.crt" -extfile "site.cnf" -extensions server
Signature ok
subject=C = CN, ST = tj, L = tj, O = tj, CN = docker.domain.com
Getting CA Private Key
[root@docker2 certs]#

[root@docker2 certs]# mkdir /ssl
[root@docker2 certs]# cp docker.domain.com.key docker.domain.com.crt root-ca.crt  /ssl
[root@docker2 certs]# ls /ssl/
docker.domain.com.crt  docker.domain.com.key  root-ca.crt
[root@docker2 certs]#

[root@docker2 ~]# mkdir /registry
[root@docker2 ~]# mv /ssl/ /registry/
[root@docker2 ~]# cd /registry/
[root@docker2 registry]# vim config.yml
[root@docker2 registry]# cat config.yml
version: 0.1
log:
  accesslog:
    disabled: true
  level: debug
  formatter: text
  fields:
    service: registry
    environment: staging
storage:
  delete:
    enabled: true
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
auth:
  htpasswd:
    realm: basic-realm
    path: /etc/docker/registry/auth/nginx.htpasswd
http:
  addr: :443
  host: https://docker.domain.com
  headers:
    X-Content-Type-Options: [nosniff]
  http2:
    disabled: false
  tls:
    certificate: /etc/docker/registry/ssl/docker.domain.com.crt
    key: /etc/docker/registry/ssl/docker.domain.com.key
health:
  storagedriver:
    enabled: true
    interval: 10s
threshold: 3

[root@docker2 registry]#


[root@docker2 registry]# mkdir auth
[root@docker2 registry]# yum -y install httpd-tools
[root@docker2 registry]# htpasswd  -Bbn testuser redhat > auth/nginx.htpasswd
[root@docker2 registry]#
[root@docker2 registry]# tree /registry/
/registry/
├── auth
│   └── nginx.htpasswd
├── config.yml
└── ssl
    ├── docker.domain.com.crt
    ├── docker.domain.com.key
    └── root-ca.crt

2 directories, 5 files
[root@docker2 registry]#

安装docker-compose编排工具:

[root@docker2 ~]# DOCKER_CONFIG=/usr/local/lib/docker/cli-plugins
[root@docker2 ~]#  mkdir -p $DOCKER_CONFIG/cli-plugins
[root@docker2 ~]# curl -SL https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
[root@docker2 ~]# chmod +x $DOCKER_CONFIG/cli-plugins
[root@docker2 ~]# docker compose version
Docker Compose version v2.27.0
[root@docker2 ~]# 


[root@docker2 ~]# mkdir /dcompose
[root@docker2 ~]# cd /dcompose
[root@docker2 dcompose]# vim docker-compose.yml
[root@docker2 dcompose]# cat docker-compose.yml
version: '3'

services:
  registry:
    image: registry
    ports:
      - "443:443"
    volumes:
      - /registry:/etc/docker/registry
      - registry-data:/var/lib/registry

volumes:
  registry-data:

[root@docker2 dcompose]#

[root@docker2 dcompose]# docker volume ls
DRIVER    VOLUME NAME
[root@docker2 dcompose]#

[root@docker2 dcompose]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4  docker.domain.com
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
[root@docker2 dcompose]#

[root@docker2 dcompose]# pwd
/dcompose
[root@docker2 dcompose]# ls
docker-compose.yml
[root@docker2 dcompose]# docker compose up -d
WARN[0000] /dcompose/docker-compose.yml: `version` is obsolete 
[+] Running 3/3
 ✔ Network dcompose_default         Cr...                            0.3s 
 ✔ Volume "dcompose_registry-data"  Created                          0.0s 
 ✔ Container dcompose-registry-1    Started                          0.7s 
[root@docker2 dcompose]#

[root@docker2 dcompose]# docker volume ls
DRIVER    VOLUME NAME
local     dcompose_registry-data
[root@docker2 dcompose]# 

[root@docker2 dcompose]# docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED          STATUS          PORTS                                             NAMES
976fbe0456cc   registry   "/entrypoint.sh /etc…"   31 seconds ago   Up 29 seconds   0.0.0.0:443->443/tcp, :::443->443/tcp, 5000/tcp   dcompose-registry-1
[root@docker2 dcompose]#

测试:
由于自行签发的 CA 根证书不被系统信任,所以我们需要将 CA 根证书 ssl/root-ca.crt 移入 /etc/docker/certs.d/docker.domain.com 文件夹中。
[root@docker2 ~]# mkdir -p /etc/docker/certs.d/docker.domain.com
[root@docker2 ~]#  cp /registry/ssl/root-ca.crt /etc/docker/certs.d/docker.domain.com/ca.crt
[root@docker2 ~]# ls /etc/docker/certs.d/docker.domain.com/ca.crt
/etc/docker/certs.d/docker.domain.com/ca.crt
[root@docker2 ~]#

登录:
[root@docker2 ~]# docker login docker.domain.com
Username: testuser
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[root@docker2 ~]# 

[root@docker2 ~]# docker tag hello-world:latest docker.domain.com/testuser/hello:v1
[root@docker2 ~]# docker push docker.domain.com/testuser/hello:v1
The push refers to repository [docker.domain.com/testuser/hello]
ac28800ec8bb: Pushed 
v1: digest: sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7 size: 524
[root@docker2 ~]#


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1706200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么Facebook Marketplace无法使用?如何解决?

Facebook Marketplace是一个允许用户买卖商品的平台&#xff0c;由于其在Facebook内的便捷性&#xff0c;它逐渐成为了一个受欢迎的在线交易市场。然而&#xff0c;做Facebook跨境电商&#xff0c;很多人会面临的情况就是无法使用Facebook Marketplace。这到底是什么原因&#…

PSexec工具横向移动

一. 其他横向工具和命令介绍 之前我们讲解了IPC,PTH,PTK,PTH&#xff0c;其实这些东西都是用来进行认证的 IPC:使用明文的账号密码进行认证 PTH:使用NTLM-hash值进行认证 PTK:使用AES秘钥进行认证 PTT:使用票据进行认证认证通过之后如何实现远程上线内网其他的机器&#xff0…

23种设计模式全面总结 | 快速复习(附PDF+MD版本)

本篇文章是对于23种设计模式的一个全面的总结&#xff0c;受限于文章篇幅无法对每个设计模式做到全面的解析&#xff0c;但几乎每个设计模式都提供了案例和类图结构&#xff0c;非常适合快速复习和在学习设计模式之前的全预习把握。 &#x1f4a1;文章的 pdf markdown 版本可通…

智能座舱-车载声学技术训练营

语音交互赋能车载智能终端&#xff0c;成为智能座舱生态构建的核心功能 曾几何时&#xff0c;至少十年前&#xff0c;车内语音交互&#xff0c;大家都认为是“智障”阶段&#xff0c;基本上除了难用作为评价找不到其他的形容词做修饰。 但是随着技术的不断发展&#xff0c;特别…

2024年哪款充电宝好?热门罗马仕、西圣、倍思充电宝全方面实测

在这个人人都离不开手机的时代&#xff0c;出门坐车需要用到手机刷卡&#xff0c;吃饭点外卖需要用到手机&#xff0c;做旅游攻略需要用到手机等等&#xff0c;如果是出门一天的话不带充电宝&#xff0c;手机基本是不能够撑回到家的&#xff0c;这时候就需要一个充电宝辅助我们…

整理前端新出的操作工具好用又好玩(Custom Formatter,Oxlint,Nuxt DevTools,component-party)

1.使用Custom Formatter 使vue3中的reactive object 在Chrome在console中更易理解的方式展现 启用步骤&#xff1a; 1.打开控制台&#xff0c;然后打开console设置 2.前往proferences中的Console&#xff0c;勾选Enable custom formatters选项 3.刷新页面 2.使用css Overv…

【NumPy】权威指南:使用NumPy的percentile函数进行百分位数计算

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

2024最新(PC+WEB+IOS+Android)即时通讯系统客户端仿默往IM源码下载

2024最新(PCWEBIOSAndroid)即时通讯系统客户端仿默往IM源码下载(总大小约2.4G&#xff09; 系统功能配置灵活、海量并发、稳定可靠、数据安全&#xff0c;2小时快速部署、数据安全、单聊群聊、系统通知等通信功能&#xff0c;支持App、PC、Web等多端快速接入。 群功能&#xf…

【小米手环7】表盘修改/制作指南

2024年了还有人用小米手环7么&#xff1f; 5月10号得到我的小米手环7nfc&#xff0c;随之开启了我对表盘制作的探索之旅。~历时18天&#xff0c;终于让我成功修改了官方表盘——荧光电子&#xff01;表盘&#xff1a;羽球人的荧光电子已上传至表盘自定义工具app。 接下来&…

将x减到0的最小操作(滑动窗口)

算法原理&#xff1a; 第一眼看到这个题的时候&#xff0c;我真的想不到它到底是怎么和滑动窗口联系起来的&#xff0c;在我的脑海里只有一个简单的双指针。直到我看过了题解&#xff0c;才明白原来是这么回事&#xff1a;先看题目是求最小操作数&#xff0c;此时你一定不要先…

二叉树顺序结构实现【堆的实现】【详细图解】

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 目录 1、二叉树的顺序结构2、堆的概念3、堆的实现3.1 堆实现的前提3.1.1 向上调整3.1.2 向下调…

网络安全行为可控定义以及表现内容简述

在数字化快速发展的今天&#xff0c;网络安全已成为国家和企业不可或缺的防线。据统计&#xff0c;网络攻击事件频发&#xff0c;给全球经济带来了巨大损失。因此&#xff0c;确保网络安全行为可控显得尤为重要。今天我们来聊聊网络安全行为可控定义以及表现内容。 网络安全行为…

音视频开发9 FFmpeg 解复用框架说明,重要API说明

一&#xff0c;播放器框架 二 常用音视频术语 容器&#xff0f;文件&#xff08;Conainer/File&#xff09;&#xff1a; 即特定格式的多媒体文件&#xff0c; 比如mp4、flv、mkv等。 媒体流&#xff08;Stream&#xff09;&#xff1a; 表示时间轴上的一段连续数据&#xff0…

维护课堂纪律的重要性

在课堂上&#xff0c;老师们是否经常遇到这样的情况&#xff1a;孩子们交头接耳&#xff0c;小动作不断&#xff0c;甚至有人偷偷玩手机&#xff1f;这些行为长让老师感到头疼&#xff0c;但作为老师&#xff0c;是否思考过&#xff0c;维护课堂纪律的重要性究竟何在&#xff1…

生信网络学院|05月31日《SOLIDWORKS Manage 产品周期管理》

课程主题&#xff1a;SOLIDWORKS Manage 产品周期管理 课程时间&#xff1a;2024年05月31日 14:00-14:30 主讲人&#xff1a;付舰 生信科技 PLM实施顾问 1、SOLIDWORKS Manage介绍 2、周期流程管理 3、产品项目管理 4、项目会议管理 5、项目问题管理 安装腾讯会议客户端…

java配置文件解析yml/xml/properties文件

XML 以mybatis.xml:获取所有Environment中的数据库并连接session为例 import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException;import javax.xml.parsers.DocumentBuilder; impo…

第十二周 5.21面向对象的三大特性(封装、继承、多态)(二)

三、多态 1.理解: (1)多态:父类型的引用存储不同子类型的对象 父类类名 引用名 new 子类类名(); 引用 对象 父类型 子类型 …

视频太大怎么压缩变小 视频太大了怎么压缩

视频作为一种多媒体形式&#xff0c;在当今社会的重要性日益凸显&#xff0c;其应用范围广泛&#xff0c;影响力深远。 但是视频文件的大小也在不断增加&#xff0c;这给存储和传输带来了很大的困扰。那么&#xff0c;当视频文件过大时&#xff0c;我们该如何将其压缩变小呢&am…

lubuntu20.04安装和使用ROS Noetic Ninjemys

Noetic Ninjemys 最后一个ROS官方支持的第一代Noetic Ninjemys 为何选择Lubuntu 熟悉我博客的朋友知道&#xff0c;我的这些分享都没有官方经费支持&#xff0c;都是在自己和志同道合朋友们共同努力下&#xff0c;走到今天。 设备陈旧&#xff0c;只能选择对系统资源需求最少…

Common Lisp笔记

在计划学习函数式编程的时候&#xff0c;我一开始打算学习的是 F#。因为我朋友就是在 DTU 上的学&#xff0c;F# 就是 DTU&#xff08;丹麦理工&#xff09;开发的。但是由于 F# 和微软的 .NET 绑定&#xff0c;而在 macOS 上&#xff0c;目前版本的 .NET 的是有些问题的&#…