Docker 入门

下载

docker 命令

帮助

# 查看 docker 命令的 options 和 command
$ docker --help

# 查看具体 command 的使用方式
$ docker command --help

查看 docker 容器信息

# 查看 docker 所有容器和镜像的数量、docker 使用的执行驱动和存储驱动以及 docker 的基本配置。
# 可以通过这个命令查看 docker 是否安装成功
$ docker info

# 查看正在运行的容器
$ docker ps

# 查看所有的容器,包括已经停止的容器
$ docker ps -a

# 查看最后 x 个容器,不论容器是否停止
$ docker ps -n 3

# 查看容器内的进程,在此约定 CONTAINER 代表 containerId 或者 containerName
$ docker top CONTAINER

# 查看一到多个容器的统计信息,例如:CPU、内存、网络I/O、存储I/O的性能和指标,常用于快速监控
$ docker status CONTAINER1 CONTAINER2 CONTAINER2 ...

# 查看本地镜像
$ docker images
REPOSITORY                                                    TAG                     IMAGE ID            CREATED             SIZE
registry.sensetime.com/plat-bigdata/superset                  0.37.2_s1               92672dae0eaf        34 minutes ago      2.62GB
registry.sensetime.com/plat-bigdata/superset                  latest                  92672dae0eaf        34 minutes ago      2.62GB
...

# Get details info of image
$ docker inspect IMAGE
$ docker inspect registry.sensetime.com/plat-bigdata/superset:latest
[
    {
        "Id": "sha256:92672dae0eaf918407418ec12c9a617bf4547b128f1b557a838b945923bfc9ff",
        "RepoTags": [
            "registry.sensetime.com/plat-bigdata/superset:0.37.2_s1",
            "registry.sensetime.com/plat-bigdata/superset:latest"
        ],
        "RepoDigests": [
            "registry.sensetime.com/plat-bigdata/superset@sha256:e6999e708c785faf2a814a478fb2c60a9a4a2ef44be3de8e49a400fb56a1531f"
        ],
        "Parent": "sha256:b942048c7428c42401b5a6e6e8f91bf6a80bb25ea4ac260d099ed5f0a0504c24",
        "Comment": "",
        "Created": "2020-11-06T07:54:27.712457746Z",
        "Container": "be51ad23bcf8513571d47acb7553976d93dea510a9485c9f6bcae349ee920b49",
        "ContainerConfig": {
            "Hostname": "be51ad23bcf8",
            "Domainname": "",
            "User": "superset",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "8080/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "LANG=C.UTF-8",
                "GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D",
                "PYTHON_VERSION=3.6.9",
                "PYTHON_PIP_VERSION=19.3.1",
                "PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/ffe826207a010164265d9cc807978e3604d18ca0/get-pip.py",
                "PYTHON_GET_PIP_SHA256=b86f36cc4345ae87bfd4f10ef6b2dbfa7a872fbff70608a1e43944d283fd0eee",
                "LC_ALL=C.UTF-8",
                "FLASK_ENV=production",
                "FLASK_APP=superset.app:create_app()",
                "PYTHONPATH=/app/pythonpath",
                "SUPERSET_HOME=/app/superset_home",
                "SUPERSET_PORT=8080"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "USER superset"
            ],
            "Healthcheck": {
                "Test": [
                    "CMD",
                    "curl",
                    "-f",
                    "http://localhost:8088/health"
                ]
            },
            "Image": "sha256:b942048c7428c42401b5a6e6e8f91bf6a80bb25ea4ac260d099ed5f0a0504c24",
            "Volumes": null,
            "WorkingDir": "/app",
            "Entrypoint": [
                "/usr/bin/docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "DockerVersion": "19.03.11",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "superset",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "8080/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "LANG=C.UTF-8",
                "GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D",
                "PYTHON_VERSION=3.6.9",
                "PYTHON_PIP_VERSION=19.3.1",
                "PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/ffe826207a010164265d9cc807978e3604d18ca0/get-pip.py",
                "PYTHON_GET_PIP_SHA256=b86f36cc4345ae87bfd4f10ef6b2dbfa7a872fbff70608a1e43944d283fd0eee",
                "LC_ALL=C.UTF-8",
                "FLASK_ENV=production",
                "FLASK_APP=superset.app:create_app()",
                "PYTHONPATH=/app/pythonpath",
                "SUPERSET_HOME=/app/superset_home",
                "SUPERSET_PORT=8080"
            ],
            "Cmd": null,
            "Healthcheck": {
                "Test": [
                    "CMD",
                    "curl",
                    "-f",
                    "http://localhost:8088/health"
                ]
            },
            "Image": "sha256:b942048c7428c42401b5a6e6e8f91bf6a80bb25ea4ac260d099ed5f0a0504c24",
            "Volumes": null,
            "WorkingDir": "/app",
            "Entrypoint": [
                "/usr/bin/docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 2615117997,
        "VirtualSize": 2615117997,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/dae93afb24535a09a2633ccccdd4fc34fe667e8db47ac82e0c0643fb729efab3/diff:/var/lib/docker/overlay2/25a7eb0773d349ee18eab4d23b38cca33ff862e6a6ee1db72004c69b2b77fdcc/diff:/var/lib/docker/overlay2/d149dd257bd193de2637f3aa9a2dfc77d1908acef089560990abcc4a0f3c2e67/diff:/var/lib/docker/overlay2/2a454d8f330ad35954e014f3b9722c7d4506f858db3b5d6102c6b095ba24fa87/diff:/var/lib/docker/overlay2/570dff4681ee1ee230a3661ae70816892b96972c4a497473cba832efce8eb727/diff:/var/lib/docker/overlay2/679c2adcc841361403fddd482e92bead297ded275cec45af445977f95c7aa92f/diff:/var/lib/docker/overlay2/dd34be49c0457f4717ce88affdc36c8b8838e506c7f64368265df42019f21dc1/diff:/var/lib/docker/overlay2/38d6f04e8fcc84ebd5ca9247c438394117b95c69631dc7761d86b041a8530053/diff:/var/lib/docker/overlay2/1ef1bab72956d9af4a774e827d35c90668f7fc3fad8e802e07baac4ac1f4d47e/diff:/var/lib/docker/overlay2/fbba98d71698f8fc95be8bd15effd5579ba42fa3330048ad3a7ae85d7200f99e/diff:/var/lib/docker/overlay2/93a1b31e0f84d7e9b13f6218a81ad58655936e9bd19c0576921aa837e737536a/diff:/var/lib/docker/overlay2/c5199189f51148f487c8f7d29bba9b1aa4705aa2a017aa4619457e294a007b1d/diff:/var/lib/docker/overlay2/89eca378b96f662c42da93436ff37ec91234884a519964f7cacc740700ecbe5f/diff:/var/lib/docker/overlay2/b1d2cecc8e85de533dc679f3e8194dfd90d858a650dab3fc3a75ca39e062f4a0/diff:/var/lib/docker/overlay2/19ece21c2506e439b14591b5f7f29450f236161bd7389d1be5981060e3030aa5/diff:/var/lib/docker/overlay2/9bd488d211631c1c06b9524162475b33701ba8688f23b9271691eeb7512061a1/diff:/var/lib/docker/overlay2/7483f09a6684fd64add52eb276883b5ed08d0714262ba7a068fc894bde11cce1/diff:/var/lib/docker/overlay2/7c1c694b85fced9dd6dd4e33f47435e164785418751d97fcd10e4b8351ea591b/diff:/var/lib/docker/overlay2/2e4ab11e66a8995dcc1ed3537a923cbb088be86c21f5741b09948024057e198f/diff",
                "MergedDir": "/var/lib/docker/overlay2/3aa192a3a64868758cddffe02f179d25c15a984d3dec4558680851c98fc5cbea/merged",
                "UpperDir": "/var/lib/docker/overlay2/3aa192a3a64868758cddffe02f179d25c15a984d3dec4558680851c98fc5cbea/diff",
                "WorkDir": "/var/lib/docker/overlay2/3aa192a3a64868758cddffe02f179d25c15a984d3dec4558680851c98fc5cbea/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:f2b4f0674ba3e6119088fe8a98c7921ed850c48d4d76e8caecd7f3d57721b4cb",
                "sha256:7f9bf938b0531c93cc0d860e72aac23093189889d89604ce24b95197435b76cf",
                "sha256:423d63eb4a274765e80c67db008e5cb9318b717c3a57790b75b53d3440a9f3e0",
                "sha256:bee1c15bf7e859fe57266ca0739533b44950a5e03333e751e05de04e0e6cf7d3",
                "sha256:9437609235f034beed771825bfb42b48bdec58a55fd1e89588750c426bf55c4e",
                "sha256:852a1bb5386cb2e36b22d11db7357b241d540e738cc467d9ff5f351d9dd069ce",
                "sha256:d20676915161118d1c96e60e17b4a0e90cba8f8e93de6681650ee07cb42a42c8",
                "sha256:7ebccfde7bb7f4114744d494eb182e3f2d92ce99aa7bff266b9526416e3974bf",
                "sha256:9ab20c4343be22704a3dc4309659908ae37b02ed6e6c55598b3aabc969adc19f",
                "sha256:d89df28dedc01502ab1097b6162b06b2e6fa5ce6b4b6bca40fd412b8ddff2c50",
                "sha256:da25db0a968977504450be8235a444d923c79b7282546395c2dbce8c684ef20a",
                "sha256:4e12eeda71b9563fd90eb603a5ce3310aaa5a0dfc4bc2abaeaed0011c7526c9e",
                "sha256:cfc0f58b5d92bf64dfc03e05d7a88a395e3b65a8064c679238e7129a4af086d4",
                "sha256:a7d0b89428048c8e9da0e3d97097ad828bed3c8de1e8e99392224aec13f1abaf",
                "sha256:7b06caba74db060de2373b6e029c3dd48a7f2da711908666a03d66f4ba22a08a",
                "sha256:ed66bd4e974d2cc9f7af40c010055ee0d146acaebe29c361496120af147c05ad",
                "sha256:a86a366a6e2a8c4eadac5b82871ac6f29bf85d4eda3e58b549d2a7d3517d7cbc",
                "sha256:52bfba46cfd07923d9fa07b8a1abaefe4b0e0cf49cf768e26abf439ecdf1a09b",
                "sha256:83df23365cd178d098dd779b78e2468b3ce16b4f20fbba583ff8beebd4687722",
                "sha256:b35d1d4b4affd95d575af6434da6d86bc270e48cac45e997a13974991e53bfea"
            ]
        },
        "Metadata": {
            "LastTagTime": "2020-11-06T15:54:33.059716673+08:00"
        }
    }
]


# 对容器进行详细的检查,然后返回 json 格式的配置信息,如:名称、命令、网络配置以及很多有用的数据
$ docker inspect CONTAINER
# 可以使用 -f 或者 --format 选定某项结果,其实这个参数支持完整的 GO 语言模版
$ docker inspect --format='' CONTAINER

docker 创建/启动、停止、删除容器

# 创建/启动容器
$ docker run --help
# Usage
$ docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

# run 创建、启动一个新的容器
# -i 保证容器中STDIN是开启的,尽管我们没有 attach 到容器中
# -t 为创建的容器分配一个伪 tty 终端
# -i 和 -t 两个参数同时存在,新建的容器才能提供一个交互式 shell
# /bin/bash 执行容器中的 shell
$ docker run -it ubuntu /bin/bash

# 其中 fd2dcda313a4 是容器 id,(hostname)
root@fd2dcda313a4:/#

# 退出 shell,这样容器也会跟着停止
root@fd2dcda313a4:/# exit

# 创建守护式容器(daemonized container)
# -d docker 会将容器放到后台运行,执行完后,他不会像 -it 一样将主机的控制台
# 附着到新的shell会话上,而是仅仅返回了一个容器ID
$ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"

# 为容器命名,这样就不一定非要用容器id去访问容器了,但是容器的命名必须是唯一的
$ docker run --name test_container -i -t centos /bin/bash

# 启动容器并暴露多个端口 -p, --expose
$ docker run -p <host_port1>:<container_port1> -p <host_port2>:<container_port2> IMAGE

# 附着到正在运行容器的会话,可能需要回车才能进入会话
$ docker attach CONTAINER

# 在容器内部运行额外的进程(docker 1.3+)
# 例如进入容器内 shell 会话
$ docker exec -it CONTAINER /bin/bash
# 启动一个新的后台任务
$ docekr exec -d CONTAINER touch /etc/new-config-file

# 停止守护式容器,向 docker 容器进程发送 SIGTERM 信号
$ docker stop CONTAINER

# 如果想快速停止某个容器,还可以直接向 docker 容器进程发送 SIGKILL 信号
$ docker kill CONTAINER

# 重新启动已经停止的 container,docekr 重启的时候会沿用 docker run 时指定的参数来运行。
$ docker start CONTAINER

# 自动重启容器 --restart
# --restart=always 无论容器的退出代码是什么,docker 都会重新启动该容器
$ docker run --restart=always -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
# --restart=on-failure 只有当容器的退出代码为非0值的时候才会自动重启,除此之外还可以接收一个重启次数的参数
$ docker run --restart=on-failure:5 -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"

# 删除容器(从这里开始,CONTAINER 代表容器ID/容器名)
$ docker rm CONTAINER

# 删除镜像
$ docker rmi IMAGE_ID
# 如果镜像被多个库引用(IMAGE_ID 相同),只会 Untag Image,而非删除镜像
$ docker rmi IMAGE_NAME:TAG

docker 容器日志

# 获取 docker 内部最新的日志
$ docker logs CONTAINER

# -f 滚动输出日志,类似 tail -f
# --tail 10 返回最后10条日志,每次
$ docker logs -f --tail 10 CONTAINER

# docker1.6起,可以控制 docekr 所用的日志驱动,在 docker run 时,使用 --log-driver 进行控制
# --log-driver 默认 json-file 还有其他可用选项,如:
# syslog 该选项禁用 docker logs 命令,并将所有容器的日志输出都重定向到 syslog
# none 该选项也会禁用所有容器中的日志,导致 docker logs 不可用
# 1.8 之后还有 fluentd 日志轮转驱动等等
$ docker run --log-driver="syslog" -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"

# 容器最多有三个日志文件,每个日志文件最大500m
$ docker run --log-driver="json-file" --log-opt max-file=3 --log-opt max-size=500m -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"

使用 Dockerfile 构建镜像

Context

通过Dockerfile的和docker build去构建镜像更具备可重复性、透明性以及幂等性,不推荐通过docker commit构建镜像。

# 创建一个目录用来保存 Dockerfile
$ mkdir contextDir
$ cd contextDir
$ touch Dockerfile

这个目录就是我们的构建环境(context/build context),docker 会把contextDir中的所有文件/文件夹保存到类似/var/lib/docker/tmp/docker-builder833199817目录下,这样在构建镜像的时候就可以直接访问,而对于不在上下文环境中的文件,docker 是访问不到的。

编写 Dockerfile

$ vim Dockerfile

# 指定 base image
FROM ubuntu:14.04
# 指定镜像作者和电子邮件
MAINTAINER zhangqiang "[email protected]"
# RUN指令会在当前镜像中运行指定的命令
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' \
    >/usr/share/nginx/html/index.html
# 该容器内的应用程序将使用容器的指定端口,但这并不意味着你可以访问容器中运行服务的端口。
# 可以指定多个 EXPOSE 向外部公开多个端口
EXPOSE 80

Dockerfile是由一系列指令和参数组成的。每条指令,如FROM都必须为大写字母,且后面要跟随一个参数。Dockerfile中的指令会按顺序从上到下执行,所以应该根据需要合理安排指令的顺序。

每条指令都会创建一个新的镜像层并commit。docker 执行 Dockerfile 的流程如下:

  • Docker 从基础镜像运行一个容器(因此第一条指令必须是 FROM)
  • 每执行一条指令,对容器作出修改,并执行类似 docker commit 的操作,提交一个新的镜像层。
  • Docker 基于刚提交的镜像运行一个新容器
  • 执行下一条指令,直到所有指令都执行完毕。

从上面可以看出,即便某条指令执行失败了,也会得到一个可以使用的镜像(只要有一条指令执行成功),这样就可以基于镜像启动一个具备交互功能的容器进行调试,查找用户指令失败的原因。

出于安全原因,docker 并不会自动打开运行服务的端口,而是需要用户在使用docker run运行容器的时候指定需要打开的端口。

Shell vs Exec

对于 Docker RUNENRTYPOINT或者CMD有两种形式的写法:

  • Shell Form
    RUN "apt-get intsall -y nginx"
    
  • Exec Form
    # 这种方式,需要使用一个数组来指定要运行的命令和传递给该命令的每个参数。
    RUN ["apt-get", "intsall", "-y", "nginx"]
    

默认情况下,RUN指令会在Shell里使用命令包装器/bin/sh -c来执行,如果是在一个不支持Shell的平台上运行或者不希望在Shell中运行(比如避免Shell字符串篡改),也可以使用Exec格式的RUN指令,Exec形式, 与Shell形式不同,Exec形式不会调用/bin/sh -c,这意味着正常的Shell处理不会发生。例如,ENTRYPOINT [ "echo", "$HOME" ]不会在$HOME上进行变量替换。如果想要进行Shell处理,可以使用Shell形式直接执行命令,例如:ENTRYPOINT echo "$HOME",而ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=$RUN_ENV"]不会进行$RUN_ENV的变量替换。不过把命令写在脚本里面,比如ENTRYPOINT ["./entrypoint.sh"],这样就可以进行变量替换了。

CMD vs ENTRYPONIT

简单理解就是CMD可以被重写,而Entrypoint是不可以被重写的,除非启动 Container 的时候添加--entrypoint。所以一般是把不希望修改的executable命令写到Entrypoint,把可以修改的默认参数写到CMD,这样就更容易其他人在docker run的时候动态修改默认参数,但是不能修改执行命令。

Note: 当 Dockerfile 中没有CMDENTRYPONIT时,启动容器会报错。对于 KubernetesK8s Command <=> Docker EntrypointK8s Args <=> Docker Cmd

构建镜像

执行docker build命令时,Dockerfile中的所有指令都会被执行并且提交,并且在该命令成功结束后返回一个新镜像。

$ cd contextDir

# -t 指定仓库和镜像名
# 也可以在创建镜像的过程中指定标签,语法为"镜像名:标签",如果不指定标签则默认为latest
# . 表示上下文环境是当前目录,也可以用绝对路径,docker 默认在指定的 contextDir 的
# 根目录下查找 dockerfile
$ docker build -t="myrepository/myimage:v1" .

Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM ubuntu:14.04
14.04: Pulling from library/ubuntu
72c01b436656: Pull complete 
65584f5f70ee: Pull complete 
dc9874b52952: Pull complete 
86656bbaa6fd: Pull complete 
7fe6916ab382: Pull complete 
Digest: sha256:cb96ec8eb632c873d5130053cf5e2548234e5275d8115a39394289d96c9963a6
Status: Downloaded newer image for ubuntu:14.04
 ---> c32fae490809
Step 2/5 : MAINTAINER zhangqiang "[email protected]"
 ---> Running in 4f9718652c27
Removing intermediate container 4f9718652c27
 ---> 7b453722693d
Step 3/5 : RUN apt-get update && apt-get install -y nginx
 ---> Running in 5cff91f7fd39
Ign http://archive.ubuntu.com trusty InRelease
Get:1 http://security.ubuntu.com trusty-security InRelease [65.9 kB]
Get:2 http://archive.ubuntu.com trusty-updates InRelease [65.9 kB]
Get:3 http://security.ubuntu.com trusty-security/universe Sources [98.6 kB]
...
...
...
Removing intermediate container 5cff91f7fd39
 ---> 342eafd8c1c3
Step 4/5 : RUN echo 'Hi, I am in your container'     >/usr/share/nginx/html/index.html
 ---> Running in f68817f0ba78
Removing intermediate container f68817f0ba78
 ---> b7a7edcb3c66
Step 5/5 : EXPOSE 80
 ---> Running in 12d9168d6c2a
Removing intermediate container 12d9168d6c2a
 ---> c208ff11437a
Successfully built c208ff11437a
Successfully tagged myrepository/myimage:v1

# 可以同时指定多个标签
$ docker build -t="myrepository/myimage:v1" -t "myrepository/myimage:latest" .

# -f 指定 Dockerfile,这里可以起其他的名字,但是必须在上下文环境中
$ docker build -t="myrepository/myimage:v1" -f ./path/other.dockerfile .

# 也可以指定一个 Git 地址(假设Dockerfile在项目的根目录)作为 contextDir,如
$ docker build -t="myrepository/myimage:v1" [email protected]:myrepository/contextDir

如果构建上下文的根目录下存在.dockerignore命名的文件,每一行是一个匹配项(Go语言的filepath),匹配到的文件不会上传到Docker daemon中。与.gitignore比较类似。

Dockerfile 和构建缓存

docker 的每一步的构建过程都会将结果提交为镜像,它会将之前的镜像层视作缓存,即最后一个成功的镜像。如果在step4出错,修改Dockerfile之后再次构建会直接从step4开始。

有时候需要确保构建的过程不能使用缓存,例如已经缓存到了step3。即apt-get update,如果使用缓存,那么docker就不会更新apt的缓存了。可以使用docker run--no-cache忽略Dockerfile的构建缓存

$ docker build -t="myrepository/myimage:v1" --no-cache .

构建缓存的好处是,我们可以实现简单的Dockerfile模版(比如在Dockerfile文件顶部增加包仓库或者更新包,从而尽可能保证缓存命中)。例如在Dockerfile文件顶部使用相同的指令集模版:

FROM ubuntu:14.04
MAINTAINER zhangqiang "[email protected]"
# 设置一个名为REFRESHED_AT的环境变量,表示镜像模板最后的更新时间
ENV REFRESHED_AT 2018-09-14
RUN apt-get -qq update

Docker Compose

Overview of Docker Compose

实战

打包一个日志输出程序的镜像

准备

# 找个 java 镜像
$ docker search java
NAME                                         DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
node                                         Node.js is a JavaScript-based platform for s…   7176                [OK]                
tomcat                                       Apache Tomcat is an open source implementati…   2318                [OK]                
java                                         Java is a concurrent, class-based, and objec…   1960                [OK]                
openjdk                                      OpenJDK is an open-source implementation of …   1562                [OK]  
...

# 提前拉到本地
$ docker pull java:8

# 看下内部情况
$ docker run -it java:8 /bin/bash
# 看下操作系统
root@f793c1e059ad:/# cat /etc/issue
Debian GNU/Linux 8 \n \l
# 看下系统内核
root@f793c1e059ad:/# cat /proc/version 
Linux version 4.9.93-linuxkit-aufs (root@856d34d1168e) (gcc version 6.4.0 (Alpine 6.4.0) ) #1 SMP Wed Jun 6 16:55:56 UTC 2018
# 看下 java 版本
root@f793c1e059ad:/# java -version
openjdk version "1.8.0_111"
OpenJDK Runtime Environment (build 1.8.0_111-8u111-b14-2~bpo8+1-b14)
OpenJDK 64-Bit Server VM (build 25.111-b14, mixed mode)
# 好,假装看懂了,退出
root@f793c1e059ad:/# exit

构建镜像

# 构建上下文环境
$ mkdir log4j_jsonlogs
$ cd log4j_jsonlogs

# 把依赖 jar 放到上下文环境
$ mv /Users/zhangqiang/IdeaProjects/jsonlogs/out/artifacts/jsonlogs_jar/target/jsonlogs.jar .

# 编写 dockerfile
$ vim log4j2_logs.dockerfile
# 指定基础镜像
FROM java:8
# 作者信息
MAINTAINER zhangqiang "[email protected]"
# 在容器内部创建一个文件夹
RUN mkdir /home/log4j
# 将 contextDir 中的 jsonlogs.jar 拷贝到容器的 /home/log4j 目录下
# 注意: source 文件路径是相对于 contextDir 的,并且 docker 只能访问 contextDir 中的文件,如果访问 contextDir
# 之外的文件会出现 COPY failed: Forbidden path outside the build context: ../test (),如果想把 
# contextDir 中的所有文件都拷贝到镜像内可以使用`COPY . /home/log4j`
COPY jsonlogs.jar /home/log4j
# 对外暴露端口
EXPOSE 80
# 当启动容器的时候执行的命令
CMD ["java", "-jar", "/home/log4j/jsonlogs.jar"]

# 构建镜像
$ docker build -t="myrepository/log4j2_logs:v1" -f ./log4j2_logs.dockerfile .

# 查看是否成功打进去了
$ docker run -it myrepository/log4j2_logs:v1 /bin/bash
root@2745f757d0bf:/home# ls -l /home/log4j/
-rw-r--r-- 1 root root 4335852 Mar 21 05:15 jsonlogs.jar

# 后台运行,并将宿主机的 4000 端口映射到容器暴露的 80 端口  
$ docker run --name log4j2 -d -p 4000:80 --log-driver=json-file myrepository/log4j2_logs:v1

# 查看输出日志
$ docker logs log4j2
{"thread":"main","level":"ERROR","loggerName":"com.rich.LogGenerator","message":"java.lang.IllegalArgumentException: error log\n\tat com.rich.LogGenerator.nestedJsonLogs(LogGenerator.java:30)\n\tat com.rich.LogGenerator.main(LogGenerator.java:17)\n","endOfBatch":false,"loggerFqcn":"org.apache.logging.log4j.spi.AbstractLogger","instant":{"epochSecond":1553157695,"nanoOfSecond":647000000},"threadId":1,"threadPriority":5}
2019-03-21 08:41:35.035 com.rich.LogGenerator ERROR java.lang.IllegalArgumentException: error log
	at com.rich.LogGenerator.nestedJsonLogs(LogGenerator.java:30)
	at com.rich.LogGenerator.main(LogGenerator.java:17)
{"thread":"main","level":"INFO","loggerName":"com.rich.LogGenerator","message":{"girlfriend":"唐xx","province":"山东","name":"张x","age":"24"},"endOfBatch":false,"loggerFqcn":"org.apache.logging.log4j.spi.AbstractLogger","instant":{"epochSecond":1553157695,"nanoOfSecond":787000000},"threadId":1,"threadPriority":5}
...

将 docker image 上传到内网 docker 服务器

# 在公网机器
$ docker pull docker.elastic.co/beats/filebeat:6.4.0

# 查看本地镜像
$ docker images
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
docker.elastic.co/beats/filebeat   6.4.0               9ef2f516cfe7        6 months ago        291MB

# 将本地镜像保存成 tar 文件
$ docker save -o filebeat.tar docker.elastic.co/beats/filebeat:6.4.0

# 将 tar 文件上传到内网服务器

# 加载到内网 docker
$ docker load -i filebeat.tar

# 查看本地 registry 地址,如果修改了 registry 地址记得重启服务才生效
$ vim /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=cgroupfs"],
  "registry-mirrors": [],
  "insecure-registries": ["192.168.51.35:5000"],
  "debug": true,
  "experimental": false
}

# 注意:insecure-registries 不需要执行登录操作,如果有认证的话要先登录
$ docker login $IMAGE_REGISTRY -u $RES_USER -p $RES_PASSWD

# 重新打标签
$ docker tag docker.elastic.co/beats/filebeat:6.4.0 192.168.51.35:5000/filebeat:6.4.0

# 查看本地镜像,可以看到相同 image id 不同库的两个镜像了
$ docker images
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
docker.elastic.co/beats/filebeat   6.4.0               9ef2f516cfe7        6 months ago        291MB
192.168.51.35:5000/filebeat        6.4.0               9ef2f516cfe7        6 months ago        291MB

# 上传到本地 registry
$ docker push 192.168.51.35:5000/filebeat:6.4.0
The push refers to repository [192.168.51.35:5000/filebeat]
be5402f062d8: Pushed 
175d99a09f10: Pushed 
853290f56c1d: Pushed 
4f90788cf2a5: Pushed 
83bce82c7799: Pushed 
9ea2263c9207: Pushed 
1d31b5806ba4: Pushed 
6.4.0: digest: sha256:739453f83c8c707c589d4e28c7a749581ea9d0511fd1ba2e3c97ae015800e103 size: 1786

qin

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏

打开支付宝扫一扫,即可进行扫码打赏哦