Docker学习笔记总结

pdf版本笔记的下载地址: Docker学习笔记01(访问密码:3834)

Docker的安装

Docker的基本组成

在这里插入图片描述

首先要明确镜像(image),容器(container)仓库(repository)的概念:

  • 镜像(image): Docker镜像(image)就是一个只读的模板,用于创建 Docker 容器.

    Docker镜像和容器之间的关系可以类比为Java中类和对象的关系,一个镜像可以创建很多容器.

  • 容器(container): Docker容器(container)容器是用镜像创建的运行实例.它可以被启动,开始,停止,删除.每个容器之间是相互隔离的.可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序。

  • 仓库(repository): 仓库(repository)是集中存放镜像文件的场所,可以分为为公开仓库(public)和私有仓库(private)两种形式.最大的公开仓库是DockerHub.

Docker的安装

理论上Docker只能安装在Linux环境下,Windows系统和Macos系统的Docker本质上是安装了一个Linux虚拟机,因此本教程演示在Centos上安装Docker,在其它Linux发行版上安装Docker的方式大同小异,见Install Docker Engine | Docker Documentation

亲测使用(Windows Subsystem for Linux)的Ubuntu系统是安装不上Docker的.

演示系统环境

目前Docker要求CentOs版本在运CentOS 7以上,系统为64位,系统内核版本为 3.10 以上.使用下列命令可以查看当前操作系统的内核和版本信息:

  • 使用uname -r命令查看当前操作系统的内核版本号,输出如下:

    3.10.0-1127.19.1.el7.x86_64
    
  • 使用cat /etc/os-release命令查看版本信息,输出如下:

    NAME="CentOS Linux"
    VERSION="7 (Core)"
    ID="centos"
    ID_LIKE="rhel fedora"
    VERSION_ID="7"
    PRETTY_NAME="CentOS Linux 7 (Core)"
    ANSI_COLOR="0;31"
    CPE_NAME="cpe:/o:centos:centos:7"
    HOME_URL="https://www.centos.org/"
    BUG_REPORT_URL="https://bugs.centos.org/"
    
    CENTOS_MANTISBT_PROJECT="CentOS-7"
    CENTOS_MANTISBT_PROJECT_VERSION="7"
    REDHAT_SUPPORT_PRODUCT="centos"
    REDHAT_SUPPORT_PRODUCT_VERSION="7"
    

安装步骤

确认系统环境满足条件后进行安装,安装步骤如下:

  1. 安装gcc相关环境

    sudo yum -y install gcc
    sudo yum -y install gcc-c++
    
  2. 卸载旧版本Docker

    sudo yum remove docker \
                      docker-client \
                      docker-client-latest \
                      docker-common \
                      docker-latest \
                      docker-latest-logrotate \
                      docker-logrotate \
                      docker-engine
    
  3. 设置yum仓库

    yum install -y yum-utils
    
    # Docker官方yum仓库,国内有可能访问不上
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    
    # 如果访问不上,就运行下面备注是的这一句,设置的是阿里云镜像Docker的yum仓库
    # yum-config-manager --add-repo http://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo
    
  4. 更新yum软件包索引

    yum makecache fast
    
  5. 安装Docker CE

    yum install docker-ce docker-ce-cli containerd.io
    
  6. 启动Docker

    systemctl start docker
    
  7. 测试命令

    运行docker version输出如下

    Client: Docker Engine - Community
     Version:           20.10.3
     API version:       1.41
     Go version:        go1.13.15
     Git commit:        48d30b5
     Built:             Fri Jan 29 14:34:14 2021
     OS/Arch:           linux/amd64
     Context:           default
     Experimental:      true
    
    Server: Docker Engine - Community
     Engine:
      Version:          20.10.3
      API version:      1.41 (minimum version 1.12)
      Go version:       go1.13.15
      Git commit:       46229ca
      Built:            Fri Jan 29 14:32:37 2021
      OS/Arch:          linux/amd64
      Experimental:     false
     containerd:
      Version:          1.4.3
      GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939b
     runc:
      Version:          1.0.0-rc92
      GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7aff
     docker-init:
      Version:          0.19.0
      GitCommit:        de40ad0
    

    运行docker run hello-world输出如下:

    Hello from Docker!
    This message shows that your installation appears to be working correctly.
    
    To generate this message, Docker took the following steps:
     1. The Docker client contacted the Docker daemon.
     2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
        (amd64)
     3. The Docker daemon created a new container from that image which runs the
        executable that produces the output you are currently reading.
     4. The Docker daemon streamed that output to the Docker client, which sent it
        to your terminal.
    
    To try something more ambitious, you can run an Ubuntu container with:
     $ docker run -it ubuntu bash
    
    Share images, automate workflows, and more with a free Docker ID:
     https://hub.docker.com/
    
    For more examples and ideas, visit:
     https://docs.docker.com/get-started/
    

    运行docker images输出如下:

    REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
    hello-world   latest    bf756fb1ae65   13 months ago   13.3kB
    

    上述命令都正常执行不报错,说明Docker安装成功

  8. (如果有需要的话)卸载Docker的命令:

    systemctl stop docker
    yum -y remove docker-ce docker-ce-cli containerd.io
    rm -rf /var/lib/docker
    

配置阿里云镜像加速服务

安装好Docker后,默认是从DockerHub上下载镜像的,对于国内用户来说,下载会很慢(真的很慢),因此需要国内的容器镜像服务,配置容器镜像服务的步骤如下:

  1. 注册阿里云账号,进入容器镜像服务,获得自己的镜像加速器地址,比如我的镜像加速器地址就是https://2mpejt6c.mirror.aliyuncs.com

    在这里插入图片描述

  2. 运行下面命令,配置镜像加速器:

    sudo mkdir -p /etc/docker
    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
      "registry-mirrors": ["https://2mpejt6c.mirror.aliyuncs.com"]		#换成你自己的镜像加速器地址
    }
    EOF
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    

这样镜像加速器就配置好了

运行 HelloWorld的过程中发生了什么

运行docker run hello-world可以下载并启动一个hello-world容器,其中的运行过程如下:

开始
Docker在本机中寻找该镜像
本机是否有该镜像?
去Docker Hub上查找该镜像
Docker Hub上是否能找到?
以该镜像为模板生产实例运行
返回失败错误,查不到该镜像
下载该镜像到本地

Docker与VM的比较

为什么Docker比较VM快

  1. Docker有着比虚拟机更少的抽象层,运行在Docker容器上的程序直接使用的都是实际物理机的硬件资源.
  2. Docker利用的是宿主机的内核,而不需要Guest OS.

在这里插入图片描述

虚拟机(VM)容器(Dcoker)
占用磁盘空间非常大,GB级小,MB甚至KB级
启动速度慢,分钟级快,秒级
运行形态运行于Hypervisor上直接运行在宿主机内核上
并发性一台宿主机上十几个,最对几十个上百个,甚至数千个
性能逊于宿主机接近宿主机本地进程
资源利用率

Docker命令

常用Dcoker命令

帮助命令

docker version 		# 显示 Docker 版本信息
docker info 		# 显示 Docker 系统信息,包括镜像和容器数
docker --help 		# 帮助

镜像命令

  1. docker images: 列出本地主机上的镜像,运行该命令得到一个表格

    REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
    hello-world   latest    bf756fb1ae65   13 months ago   13.3kB
    

    表中各项的意义:

    意义
    REPOSITORY镜像的仓库源
    TAG镜像的标签
    IMAGE ID镜像的ID
    CREATED镜像创建时间
    SIZE镜像大小

    常用参数:

    参数意义
    -a列出所有镜像
    -q静默模式,只显示镜像id
    --digests显示镜像的摘要信息
  2. docker search: 本质上与在Docker Hub仓库中搜索镜像是一致的,不常用.

  3. docker pull 镜像名[:tag]: 下载镜像

    下载镜像的时候是分层下载的,比如运行docker pull mysql下载mysql镜像,输出如下:

    Using default tag: latest 		# 不写tag,默认下载tag为latest
    latest: Pulling from library/mysql
    54fec2fa59d0: Already exists 	# 分层下载,前几层已经存在,故而不需要下载
    bcc6c6145912: Already exists
    951c3d959c9d: Already exists
    05de4d0e206e: Already exists
    319f0394ef42: Already exists
    d9185034607b: Already exists
    013a9c64dadc: Already exists
    42f3f7d10903: Pull complete		# 仅需要下载不存在的层即可
    c4a3851d9207: Pull complete
    
    Digest:sha256:61a2a33f4b8b4bc93b7b6b9e65e64044aaec594809f818aeffbff69a893d1944 # 摘要信息
    Status: Downloaded newer image for mysql:latest
    docker.io/library/mysql:latest 			# 镜像真实位置
    

    也可以在镜像名后加tag,指定版本,如:docker pull mysql:5.7

  4. docker rmi 镜像id: 删除镜像

    命令意义
    docker rmi -f 镜像id删除单个镜像
    docker rmi -f 镜像名:tag 镜像名:tag删除多个镜像
    docker rmi -f $(docker images -qa)结合docker images命令,删除全部镜像

容器命令

  1. docker run: 新建容器并启动

    格式: docker run [OPTIONS] IMAGE [COMMAND][ARG...]

    常用参数:

    参数意义例子
    --name给容器起名--name="container01"
    -d以后台方式运行容器,并返回容器的id
    -i以交互模式运行容器,通常和-t一起使用
    -t重新指定一个终端,通常和-i一起使用docker run -it centos /bin/bash
    -P随机端口映射-P 3306将容器的3306端口随机映射到宿主机的某个端口
    -p指定端口映射-p 3310:3306将容器的3306端口映射到宿主机的3310端口
  2. docker ps: 列出所有(正在运行的)容器

    常用参数:

    参数意义
    -a列出所有容器(包括历史运行过的容器)
    -l显示最近创建的容器
    -n=?显示最近n个创建的容器
    -q静默模式,只显示容器id
  3. 退出容器

    命令意义
    exit停止容器并退出交互模式
    Ctrl+P+Q不停止容器并退出交互模式
  4. 启动停止容器

    命令意义
    docker start (容器id or 容器名)启动容器
    docker restart (容器id or 容器名)重启容器
    docker stop (容器id or 容器名)停止容器
    docker kill (容器id or 容器名)强制停止容器
  5. docker rm 容器id: 删除容器

    命令意义
    docker rm 容器id删除单个容器
    docker rm 容器id 容器id删除多个容器
    docker rm -f $(docker ps -qa)结合docker ps命令,删除全部容器

其他常用命令

  1. 后台启动容器: docker run -d 容器名

    问题: 直接使用docker run -d centos以后台方式启动,结果发现该容器马上停止了,这是为什么呢?

    答案: Docker容器要想后台运行,就必须有一个前台进程.容器运行的命令如果不是那些一直挂起的命令,就会在命令执行完毕后自动退出.

  2. 查看日志: docker logs 容器id

    常用参数:

    参数意义例子
    -t显示时间戳
    -f显示最近创建的容器
    --tail 打印条数显示多少条-tail 10

    例子: docker logs -tf --tail 10 c8530dbbe3b4

  3. 查看容器中运行的进程信息: docker top 容器id

    使用docker top 8dc189995333命令查看某容器中运行的进程信息,输出结果如下:

    UID		PID		PPID		C		STIME		TTY		TIME		CMD
    polkitd	22370	22350		0		00:36		?		00:00:03	mysqld
    
  4. 查看容器/镜像的元数据: docker inspect 容器id

    使用docker inspect 8dc189995333命令查看某容器的元数据如下:

    [
        {
            "Id": "8dc18999533348d6aeaa31b24f3d592f8c2a1738636d3a2eb53bdaf3efbbc4c0",
            "Created": "2021-02-07T10:27:37.756580818Z",
            "Path": "docker-entrypoint.sh",
            "Args": [...],
            "State": {"Status": "running"...},
            "Image": "sha256:c8562eaf9d81c779cbfc318d6e01b8e6f86907f1d41233268a2ed83b2f34e748",
            "ResolvConfPath": "/var/lib/docker/containers/8dc18999533348d6aeaa31b24f3d592f8c2a1738636d3a2eb53bdaf3efbbc4c0/resolv.conf",
            "HostnamePath": "/var/lib/docker/containers/8dc18999533348d6aeaa31b24f3d592f8c2a1738636d3a2eb53bdaf3efbbc4c0/hostname",
            "HostsPath": "/var/lib/docker/containers/8dc18999533348d6aeaa31b24f3d592f8c2a1738636d3a2eb53bdaf3efbbc4c0/hosts",
            "LogPath": "/var/lib/docker/containers/8dc18999533348d6aeaa31b24f3d592f8c2a1738636d3a2eb53bdaf3efbbc4c0/8dc18999533348d6aeaa31b24f3d592f8c2a1738636d3a2eb53bdaf3efbbc4c0-json.log",
            "Name": "/musing_jepsen",
            "RestartCount": 0,
            "Driver": "overlay2",
            "Platform": "linux",
            "MountLabel": "",
            "ProcessLabel": "",
            "AppArmorProfile": "",
            "ExecIDs": null,
            "HostConfig": {"Binds": null...},
            "GraphDriver": {"Name": "overlay2"...},
            "Mounts": [...],
            "Config": {"Hostname": "8dc189995333"...},
            "NetworkSettings": {"Bridge": ""...}
    ]
    
  5. 进入正在运行的容器

    命令意义
    docker exec -it 容器id bashShell进入容器并打开一个新终端,启动新线程
    docker attach 容器id进入容器并打开启动命令的终端,不会启动新进程
  6. 从容器内拷贝文件到主机上: docker cp 容器id:容器内路径 目的主机路径

Docker命令小结

在这里插入图片描述

命令意义意义
attachAttach to a running container当前shell下attach连接指定运行镜像
buildBuild an image from a Dockerfile通过Dockerfile定制镜像
commitCreate a new image from a container changes提交当前容器为新的镜像
cpCopy files/folders from the containers filesystem to the host path从容器中拷贝指定文件或者目录到宿主机中
createCreate a new container创建一个新的容器,同run,但不启动容器
diffInspect changes on a container’s filesystem查看Docker容器变化
eventsGet real time events from the server从Docker服务获取容器实时事件
execRun a command in an existing container在已存在的容器上运行命令
exportStream the contents of a container as a tar archive导出容器的内容流作为一个tar归档文件(对应import
historyShow the history of an image展示一个镜像形成历史
imagesList images列出系统当前镜像
importCreate a new filesystem image from the contents of a tarball从 tar包中的内容创建一个新的文件系统映像(对应export)
infoDisplay system-wide information显示系统相关信息
inspectReturn low-level information on a container查看容器详细信息
killKill a running containerkill指定Docker容器
loadLoad an image from a tar archive从一个tar包中加载一 个镜像(对应save)
loginRegister or Login to the docker registry server注册或者登陆一个Docker源服务器
logoutLog out from a Docker registry server从当前Docker registry退出
logsFetch the logs of a container输出当前容器日志信息
portLookup the public-facing port which is NAT-ed to PRIVATE_PORT查看映射端口对应的容器内部源端口
pausePause all processes within a container暂停容器
psList containers列出容器列表
pullPull an image or a repository from the docker registry server从Docker镜像源服务器拉取指定镜像或者库镜像
pushPush an image or a repository to the docker registry server推送指定镜像或者库镜像至Docker源服务器
restartRestart a running container重启运行的容器
rmRemove one or more containers移除一个或者多个容器
rmiRemove one or more images移除一个或多个镜像(无容器使用该镜像才可删除,否则需删除相关容器才可继续或-f强制删除
runRun a command in a new container创建一个新的容器并运行一个命令
saveSave an image to a tar archive保存一个镜像为一个tar包(对应load)
searchSearch for an image on the Docker Hub在Docker Hub中搜索镜像
startStart a stopped containers启动容器
stopStop a running containers停止容器
tagTag an image into a repository给源中镜像打标签
topLookup the running processes of a container查看容器中运行的进程信息
unpauseUnpause a paused container取消暂停容器
versionShow the docker version information查看Docker版本号
waitBlock until a container stops, then print its exit code截取容器停止时的退出状态值

练习1: 使用Docker安装Nginx

  1. 搜索镜像: docker search nginx,输出结果如下:

    NAME                               DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
    nginx                              Official build of Nginx.                        14445     [OK]
    jwilder/nginx-proxy                Automated Nginx reverse proxy for docker con…   1965                 [OK]
    richarvey/nginx-php-fpm            Container running Nginx + PHP-FPM capable of…   807                  [OK]
    jc21/nginx-proxy-manager           Docker container for managing Nginx proxy ho…   146
    linuxserver/nginx                  An Nginx container, brought to you by LinuxS…   141
    tiangolo/nginx-rtmp                Docker image with Nginx using the nginx-rtmp…   113                  [OK]
    ...
    
  2. 拉取镜像: docker pull nginx,输出如下:

    Using default tag: latest
    latest: Pulling from library/nginx
    a076a628af6f: Already exists
    0732ab25fa22: Pull complete
    d7f36f6fe38f: Pull complete
    f72584a26f32: Pull complete
    7125e4df9063: Pull complete
    Digest: sha256:10b8cc432d56da8b61b070f4c7d2543a9ed17c2b23010b43af434fd40e2ca4aa
    Status: Downloaded newer image for nginx:latest
    docker.io/library/nginx:latest
    
  3. 启动容器: docker run -d --name mynginx -p 3500:80 nginx,输出如下:

    58d4ab1e9f71cb183cc68e98d5cd2ceb6e856b65f85dd70fb378afb254f6c0d4	# 这是该容器的container ID
    
  4. 测试访问: curl localhost:3500,输出如下:

    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
        body {
            width: 35em;
            margin: 0 auto;
            font-family: Tahoma, Verdana, Arial, sans-serif;
        }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    

    得到上面结果说明Nginx启动成功.

  5. 查看容器状态: docker ps,输出如下:

    CONTAINER ID	IMAGE	COMMAND					CREATED				STATUS				PORTS					NAMES
    58d4ab1e9f71   	nginx	"/docker-entrypoint.…" 	About a minute ago	Up About a minute 	0.0.0.0:3500->80/tcp	mynginx
    
  6. 进入容器,查看index.html

    1. 进入容器: docker exec -it mynginx /bin/bash

    2. 查询nginx的路径: whereis nginx,输出如下:

      /usr/sbin/nginx 
      /usr/lib/nginx 
      /etc/nginx 
      /usr/share/nginx
      
    3. 查看index.html: cat /usr/share/nginx/html/index.html,输出如下:

      <!DOCTYPE html>
      <html>
      <head>
      <title>Welcome to nginx!</title>
      <style>
          body {
              width: 35em;
              margin: 0 auto;
              font-family: Tahoma, Verdana, Arial, sans-serif;
          }
      </style>
      </head>
      <body>
      <h1>Welcome to nginx!</h1>
      <p>If you see this page, the nginx web server is successfully installed and
      working. Further configuration is required.</p>
      
      <p>For online documentation and support please refer to
      <a href="http://nginx.org/">nginx.org</a>.<br/>
      Commercial support is available at
      <a href="http://nginx.com/">nginx.com</a>.</p>
      
      <p><em>Thank you for using nginx.</em></p>
      </body>
      </html>
      

练习2: 使用Docker安装Tomcat

  1. 下载Tomcat镜像: docker pull tomcat
  2. 启动: docker run -d -p 8080:8080 --name tomcat01 tomcat
  3. 进入Tomcat: docker exec -it tomcat9 /bin/bash
  4. 部署项目

练习3: 使用Docker部署Elasticsearch

使用Docker部署Elasticsearch需要考虑如下几个问题:

  1. 端口暴露问题9200,9300.
  2. 数据卷的挂载问题data,plugins,conf
  3. 内存问题: "ES_JAVA_OPTS=-Xms512m -Xmx512m"

实验步骤如下:

  1. 先启动一个Elasticsearch镜像: docker run -d --name elasticsearch01 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.6.2

  2. 启动成功之后发现服务器很卡,使用docker stats命令查看容器状态:docker stats elasticsearch01,输出如下:

    CONTAINER ID	NAME 				CPU % 		MEM USAGE / LIMIT 		MEM %
    249ae46da625 	elasticsearch01		0.00% 		1.036GiB / 1.716GiB 	60.37%
    

    发现是Elasticsearch占用内存太大了.

  3. 关闭这个Elasticsearch镜像: docker stop elasticsearch01

  4. 启动Elasticsearch镜像是增加内存限制: docker run -d --name elasticsearch02 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2

  5. 使用docker stats命令查看容器状态:docker stats elasticsearch02,输出如下:

    CONTAINER ID   NAME					CPU %     	MEM USAGE / LIMIT     	MEM % 
    bea4a52fb840   elasticsearch02   	0.45%     	404.4MiB / 1.795GiB   	22.00%
    

    发现这次服务就不卡了

  6. 测试访问: curl localhost:9200,输出如下:

    {
      "name" : "bea4a52fb840",
      "cluster_name" : "docker-cluster",
      "cluster_uuid" : "-adcnQWoS3S0KR4YBsDWBA",
      "version" : {
        "number" : "7.6.2",
        "build_flavor" : "default",
        "build_type" : "docker",
        "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
        "build_date" : "2020-03-26T06:34:37.794943Z",
        "build_snapshot" : false,
        "lucene_version" : "8.4.0",
        "minimum_wire_compatibility_version" : "6.8.0",
        "minimum_index_compatibility_version" : "6.0.0-beta1"
      },
      "tagline" : "You Know, for Search"
    }
    

容器数据卷

随着Docker容器的删除,其中的数据也被删除了,对于数据库等应用来说这样不合适.

可以使用容器数据卷将数据挂在到本地,卷就是目录或者文件,存在于一个或者多个容器中,由Docker挂载到容器,但不属于联合文件系统,因此能够绕过 Union File System,提供一些用于持续存储或共享数据的特性.

卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷.

容器数据卷的特点:

  1. 数据卷可在容器之间共享或重用数据
  2. 卷中的更改可以直接生效
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷的生命周期一直持续到没有容器使用它为止

卷就是容器的持久化,以及容器间的继承和数据共享.

容器数据卷的挂载

使用docker run命令的-v参数添加数据卷,命令格式如下:

docker run -v 挂载路径映射 镜像名

根据挂载方式不同,挂载路径映射的写法不同.

指定路径挂载

对于指定路径挂载,挂载路径映射的格式是宿主机路径:容器内路径,例如:

docker run --name centos01 -it -v /home/ceshi:/home centos /bin/bash

上述命令将容器内的/home路径挂载到宿主机的/home/ceshi路径上.

使用docker inspect命令可以查看容器数据卷: docker inspect centos01,得到输出如下,可以在Mounts节点下看到挂载的容器数据卷.

[
    {
        "Id": "a36ee54bd1f03b30820490171ef6f058dea39b5c4d1350013be462b6baa6ff09",
        // ...
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/ceshi",
                "Destination": "/home",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ]
        // ...
    }
]

不论容器是否运行,在容器外对/home/ceshi的操作和在容器内对/home的操作是一致的.


下面演示使用容器数据卷将MySQL数据库中的数据持久化:

  1. 启动MySQL数据库时除了使用-v参数指定数据卷挂载,-p参数指定端口映射之外,还要使用-e参数指定MySQL的root用户的密码,见Docker Hub上MySQL镜像的页面.

    docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d \
    	-v /home/mysql/data:/var/lib/mysql \
    	-e MYSQL_ROOT_PASSWORD=123456 \
    	--name mysql01 \
    	mysql
    

    上述命令将容器内的MySQL配置文件/etc/mysql/conf.d映射到宿主机的/home/mysql/conf文件上,将容器内MySQL数据文件目录/var/lib/mysql映射到宿主机/home/mysql/data目录上.

  2. 使用docker inspect命令查看数据卷挂载信息: docker inspect mysql01,得到结果如下,可以看到端口映射和数据卷挂载信息:

    [
        {
            "Id": "b9b5aa7a5a18790c3b69ee7531c825da8585e3b7c71741cff749ad9841d9db0e",
            "Name": "/mysql01",
            // ...
            "Mounts": [		// 数据卷挂载信息
                {	// MySQL配置文件"etc/mysql/conf.d"映射到宿主机的"/home/mysql/conf"文件上
                    "Type": "bind",
                    "Source": "/home/mysql/conf",
                    "Destination": "/etc/mysql/conf.d",
                    "Mode": "",
                    "RW": true,
                    "Propagation": "rprivate"
                },
                {	// MySQL数据文件目录"/var/lib/mysql"映射到宿主机"/home/mysql/data"目录上
                    "Type": "bind",
                    "Source": "/home/mysql/data",
                    "Destination": "/var/lib/mysql",
                    "Mode": "",
                    "RW": true,
                    "Propagation": "rprivate"
                }
            ],
            // ...
            "NetworkSettings": {
                // ...
                "Ports": {		// 容器的3306端口映射到宿主机的3310端口上
                    "3306/tcp": [
                        {
                            "HostIp": "0.0.0.0",
                            "HostPort": "3310"
                        }
                    ],
                    "33060/tcp": null
                },
                // ...
            }
        }
    ]
    
  3. 使用数据库客户端工具连接宿主机的3310端口就能对容器中的MySQL数据库进行操作,在数据库中创建数据后查看宿主机的/home/mysql目录,发现存在对应的文件.

  4. 使用docker rm -f mysql01命令删除容器,发现主机的/home/mysql目录下的文件依然存在.

匿名挂载和具名挂载

出于可移植性的考虑,指定目录挂载的数据卷挂载方式不常用,因为宿主机路径是依赖于特定宿主机的,并不能够保证所有宿主机上都存在对应目录.

常见的数据卷挂载方式有两种:

  1. 匿名挂载: 指定数据卷在容器内的路径,挂载路径映射的格式是容器内路径.
  2. 具名挂载: 指定数据卷在容器内的路径并为数据卷命名,挂载路径映射的格式是卷名:容器内路径.

例如:

docker run -d -P --name nginx01 -v /etc/nginx nginx					# 匿名挂载
docker run -d -P --name nginx02 -v nginx-volume:/etc/nginx nginx	# 具名挂载

为了区分具名挂载和指定路径挂载,指定路径挂载中的宿主机路径必须是绝对路径(以/开头);而具名挂载中的卷名不得以/开头.

使用docker inspect 命令可以查看数据卷挂载信息:

  • 运行命令docker inspect nginx01,输出如下:

    [
        {
            "Id": "f483cc589ff041580e114d04b5a03324e62a24c44b68485d9ed4cf2fa75f4c31",
            "Name": "/nginx01",
            // ...
            "Mounts": [
                {	// 匿名挂载,"Name"属性为随机hash
                    "Type": "volume",		
                    "Name": "689f2a4c1b6b95079378d64fe468f256e2639ebd6aaaae2028e3cd7f33f146a0",
                    "Source": "/var/lib/docker/volumes/689f2a4c1b6b95079378d64fe468f256e2639ebd6aaaae2028e3cd7f33f146a0/_data",
                    "Destination": "/etc/nginx",
                    "Driver": "local",
                    "Mode": "",
                    "RW": true,
                    "Propagation": ""
                }
            ]
            // ...
        }
    ]
    
  • 运行命令docker inspect nginx02,输出如下:

    [
        {
            "Id": "f483cc589ff041580e114d04b5a03324e62a24c44b68485d9ed4cf2fa75f4c31",
         	"Name": "/nginx02",
            // ...
            "Mounts": [
                {	// 具名挂载,"Name"属性为指定的卷名
                    "Type": "volume",
                    "Name": "nginx-volume",
                    "Source": "/var/lib/docker/volumes/nginx-volume/_data",
                    "Destination": "/etc/nginx",
                    "Driver": "local",
                    "Mode": "z",
                    "RW": true,
                    "Propagation": ""
                }
            ],
            // ...
        }
    ]
    

使用具名挂载和匿名挂载的数据卷在宿主机的/var/lib/docker/volumes目录下.使用docker volume ls命令可以查看所有容器数据卷:

DRIVER    VOLUME NAME
local     f38b9f3afda1f8511ce46668a7edd40b581c001426ca5fc2b96a11ae9038b654
local     nginx-volume

指定读写权限

挂载路径映射后加上:读写权限可以设置容器对该数据卷的读写权限,读写权限有两种:

  1. ro: 只读权限
  2. rw: 读写权限

示例如下:

docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx:ro nginx	# 只读
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx:rw nginx	# 读写

容器间数据共享

可以通过数据卷实现容器间数据的共享,在创建容器时使用--volumes-from 父容器参数指定从父容器中继承(共享)数据卷,实现父子容器间的数据卷共享.示例如下:

  1. 创建父容器container01,指定两个匿名挂载卷/data_volume1/data_volume1:

    docker run --name container01 -v /data_volume1 -v /data_volume2 -it centos /bin/bash
    
  2. 打开另外两个ssh窗口,分别创建子容器container02, container03,使用--volumes-from container01参数指定从container01中继承数据卷.

    docker run --name container02 --volumes-from container01 -it centos /bin/bash
    docker run --name container03 --volumes-from container01 -it centos /bin/bash
    
  3. 在某个容器中修改贡献路径下的数据,另外两个容器中的数据同步发生变化.

  4. 删除某个容器,另外两个容器中的数据不受影响.

  5. 删除所有容器,宿主机内的数据仍存在.

得出结论: 容器数据卷的生命周期一直持续到没有容器使用它为止,存储在本机的文件则会一直保留.

Docker镜像

Docker镜像原理

Linux操作系统由内核空间和用户空间组成,内核空间是kernel,Linux刚启动时会加载bootfs文件系统,之后bootfs会被卸载掉.

在这里插入图片描述

Dcoker镜像通过共享bootfs层模拟出多种操作系统环境

在这里插入图片描述

镜像是分层的:

在这里插入图片描述

在下载镜像时,可以看到镜像是分层下载的: docker pull redis,输出如下:

Using default tag: latest
latest: Pulling from library/redis
a076a628af6f: Already exists
f40dd07fe7be: Pull complete
ce21c8a3dbee: Pull complete
ee99c35818f8: Pull complete
56b9a72e68ff: Pull complete
3f703e7f380f: Pull complete
Digest: sha256:0f97c1c9daf5b69b93390ccbe8d3e2971617ec4801fd0882c72bf7cad3a13494
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest

使用docker inspect命令也可以查看Docker镜像的层次结构: docker inspect redis,可以在RootFS/Layers节点下看到该镜像的层次结构:

[
    {
        "Id": "sha256:621ceef7494adfcbe0e523593639f6625795cc0dc91a750629367a8c7b3ccebb",
        // ...
        "RootFS": {
            "Type": "layers",
            "Layers": [		// 该镜像的6层结构
                "sha256:cb42413394c4059335228c137fe884ff3ab8946a014014309676c25e3ac86864",
                "sha256:8e14cb7841faede6e42ab797f915c329c22f3b39026f8338c4c75de26e5d4e82",
                "sha256:1450b8f0019c829e638ab5c1f3c2674d117517669e41dd2d0409a668e0807e96",
                "sha256:f927192cc30cb53065dc266f78ff12dc06651d6eb84088e82be2d98ac47d42a0",
                "sha256:a24a292d018421783c491bc72f6601908cb844b17427bac92f0a22f5fd809665",
                "sha256:3480f9cdd491225670e9899786128ffe47054b0a5d54c48f6b10623d2f340632"
            ]
        },
        // ...
    }
]

Docker镜像的创建

创建新镜像有两种方式: 通过docker commit命令commit容器 和 通过docker build命令根据Dockerfile创建镜像.

镜像commit

容器本质上是在镜像最上层上方附加的可写的一层.

在这里插入图片描述

可以使用docker commit命令将该层的修改固化下来,生成新的镜像,语法如下:

docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]

下面以Tomcat镜像为例,演示如何基于已有镜像生成新镜像:

  1. 默认下载的Tomcat镜像运行之后访问其8080端口会得到404错误,因为其/usr/local/tomcat/webapps目录为空.

    1. 先使用docker run -d -p 8080:8080 --name tomcat01 tomcat命令启动Tomcat容器

    2. 使用curl localhost:8080访问8080端口,得到结果如下:

      <!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/9.0.41</h3></body></html>
      

      可以看到404错误

    3. 进入docker容器,查看其/usr/local/tomcat/webapps目录,发现其内容为空:

      docker exec -it tomcat01 /bin/bash
      ls -l /usr/local/tomcat/webapps
      

      输出如下:

      total 0
      
    4. 可以看到webapps中的默认内容都在webapps.dist目录下: ls -l /usr/local/tomcat/webapps.dist

      total 20
      drwxr-xr-x  3 root root 4096 Jan 13 08:25 ROOT
      drwxr-xr-x 15 root root 4096 Jan 13 08:25 docs
      drwxr-xr-x  7 root root 4096 Jan 13 08:25 examples
      drwxr-xr-x  6 root root 4096 Jan 13 08:25 host-manager
      drwxr-xr-x  6 root root 4096 Jan 13 08:25 manager
      
  2. 因此我们启动一个新Tomcat容器,将webapps.dist目录下的内容复制到webapps目录下:

    1. 启动Tomcat容器并进入工作目录: docker run -d -p 8080:8080 --name tomcat02 tomcat

    2. 进入容器: docker exec -it tomcat02 /bin/bash

    3. webapps.dist目录下的内容复制到webapps目录下: cp -r webapps.dist/* webapps

    4. 退出容器: Ctrl+P+Q

    5. 访问8080端口: curl localhost:8080,得到输出如下:

      <!DOCTYPE html>
      <html lang="en">
          <head>
              <meta charset="UTF-8" />
              <title>Apache Tomcat/9.0.41</title>
              <link href="favicon.ico" rel="icon" type="image/x-icon" />
              <link href="tomcat.css" rel="stylesheet" type="text/css" />
          </head>
      
          <body>
              <div id="wrapper">
                  <div id="navigation" class="curved container">
                      <span id="nav-home"><a href="https://tomcat.apache.org/">Home</a></span>
                      <span id="nav-hosts"><a href="/docs/">Documentation</a></span>
                      <span id="nav-config"><a href="/docs/config/">Configuration</a></span>
                      <span id="nav-examples"><a href="/examples/">Examples</a></span>
                      <span id="nav-wiki"><a href="https://wiki.apache.org/tomcat/FrontPage">Wiki</a></span>
                      <span id="nav-lists"><a href="https://tomcat.apache.org/lists.html">Mailing Lists</a></span>
                      <span id="nav-help"><a href="https://tomcat.apache.org/findhelp.html">Find Help</a></span>
                      <br class="separator" />
                  </div>
              </div>
          	<!-- ... -->    
          </body>
      </html>
      
  3. 通过上述步骤,我们已构建了一个可访问的Tomcat容器,之后可以通过docker commit命令将上述容器打包成镜像

    docker commit -a="chenhai" -m="copy webapps.dist/* to webapps" tomcat02 my_tomcat_image
    
  4. 使用docker images命令,可以看到我们打包的my_tomcat_image镜像:

    REPOSITORY        TAG       IMAGE ID       CREATED          SIZE
    my_tomcat_image   latest    919273b2c954   14 seconds ago   654MB
    tomcat            latest    040bdb29ab37   5 weeks ago      649MB
    
  5. 使用docker inspect命令分别查看tomcat镜像和my_tomcat_image镜像的"Layers"属性,可以看出my_tomcat_image是基于tomcat基础上构建的

    • 运行docker inspect tomcat命令,输出如下:

       		[
          {
          // ...
              "RootFS": {
              "Type": "layers",
                  "Layers": [		// tomcat镜像有10层
                      "sha256:4762552ad7d851a9901571428078281985074e5ddb806979dd7ad24748db4ca0",
                      "sha256:a1f2f42922b1d3aa948f2569adebb37129941e889f13b96823a5e2aa8ecc1a8f",
                      "sha256:ef9a7b8862f4797ec08f3733ab9fc2a08c51f44e5561b087eb10a6e442599760",
                      "sha256:aa7af8a465c6b600b7151db82799f757d0029c7fb9f170faaffdc40080c525c5",
                      "sha256:7496c5e8691b7de622ad5c1e0f9b300df55e923b960fc75e7b7cf1a297a36da8",
                      "sha256:7a9b35031285d77ce1d4e73b9eabb0a8f204b0729c3f4c342bca4e57b73fbae1",
                      "sha256:500f722b156b012903f8a0af13cfb2a8521be6b2126071f4330714b6fa5bd9d7",
                      "sha256:8e2e6f2527c7c2e81725a0d539a04281d8a32b26bf2ca57fe5f5ffe20862625d",
                      "sha256:c9132b9a3fc8c30239edb774bdf01b252e1cbe945c3994e4fa12929cc93831d4",
                      "sha256:9ddc8cd8299b64510aee4f540827bf2c5cf326aa73fdd47a599a1ee0d9a8c224"
                     ]
              },
              // ...
          }
      ]
      
    • 运行docker inspect my_tomcat_image命令,输出如下:

      [
          {
              // ...
          "RootFS": {
                  "Type": "layers",
                  "Layers": [		// my_tomcat_image镜像有11层,可以看到前10层与tomcat镜像相同
                      "sha256:4762552ad7d851a9901571428078281985074e5ddb806979dd7ad24748db4ca0",
                      "sha256:a1f2f42922b1d3aa948f2569adebb37129941e889f13b96823a5e2aa8ecc1a8f",
                      "sha256:ef9a7b8862f4797ec08f3733ab9fc2a08c51f44e5561b087eb10a6e442599760",
                      "sha256:aa7af8a465c6b600b7151db82799f757d0029c7fb9f170faaffdc40080c525c5",
                      "sha256:7496c5e8691b7de622ad5c1e0f9b300df55e923b960fc75e7b7cf1a297a36da8",
                      "sha256:7a9b35031285d77ce1d4e73b9eabb0a8f204b0729c3f4c342bca4e57b73fbae1",
                      "sha256:500f722b156b012903f8a0af13cfb2a8521be6b2126071f4330714b6fa5bd9d7",
                      "sha256:8e2e6f2527c7c2e81725a0d539a04281d8a32b26bf2ca57fe5f5ffe20862625d",
                      "sha256:c9132b9a3fc8c30239edb774bdf01b252e1cbe945c3994e4fa12929cc93831d4",
                      "sha256:9ddc8cd8299b64510aee4f540827bf2c5cf326aa73fdd47a599a1ee0d9a8c224",
      				"sha256:4bf57ab98abebba174523e47d442ba9dd58213ac1e28b12df21f0f10bb708146"
                     ]
              },
              // ...
          }
      ]
      

Dockerfile

从Dockerfile构建Docker镜像的步骤:

  1. 编写Dockerfile文件
  2. docker build 构建镜像
  3. docker run

在这里插入图片描述

Dockerfile的常见指令如下:

指令意义
FROM基础镜像,指定当前新镜像是基于哪个镜像的
MAINTAINER镜像维护者的姓名混合邮箱地址
RUN容器构建时需要运行的命令
EXPOSE当前容器对外保留出的端口
WORKDIR指定在创建容器后,终端默认登录的进来工作目录,一个落脚点
ENV用来在构建镜像过程中设置环境变量
ADD将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
COPY类似ADD,拷贝文件和目录到镜像中!
VOLUME容器数据卷,用于数据保存和持久化工作
CMD指定一个容器启动时要运行的命令,dockerFile中可以有多个CMD指令,但只有最后一个生效
ENTRYPOINT指定一个容器启动时要运行的命令,和CMD一样
ONBUILD当构建一个被继承的Dockerfile时运行命令,父镜像在被子镜像继承后,父镜像的ONBUILD被触发

下面以CentOS官方镜像为例,展示一个Dockerfile的内容,见CentOS的Dockerfile:

FROM scratch					# base镜像,最基础的镜像
ADD centos-8-x86_64.tar.xz /	# 将rootfs添加到根目录并解压
LABEL org.label-schema.schema-version="1.0"     org.label-schema.name="CentOS Base Image"     org.label-schema.vendor="CentOS"     org.label-schema.license="GPLv2"     org.label-schema.build-date="20201204"
CMD ["/bin/bash"]				# 启动命令行
练习1: 构建自定义CentOS镜像

CentOS的官方Docker镜像比较精简,没有很多常用的工具,登陆后的默认路径的是根目录/.下面我们在此基础上进行修改,增加vimnet-tools,并将登陆后的默认路径修改为root用户的家目录.步骤如下:

  1. 编写Dockerfile文件:

    FROM centos						# 基于centos镜像构建		
    MAINTAINER chenh<123@qq.com>	# 作者姓名和邮箱
    ENV MYPATH /root				# 设置环境变量
    WORKDIR $MYPATH					# 设置默认登陆目录
    RUN yum -y install vim			# 安装vim
    RUN yum -y install net-tools	# 安装net-tools
    EXPOSE 80						# 暴露80端口
    CMD echo $MYPATH				# 输出调试信息				
    CMD echo "---end---"			# 输出调试信息
    CMD /bin/bash					# 进入bash
    
  2. 进入Dockerfile所在目录,执行docker build命令构建镜像:

    docker build语法如下:

    docker build -f dockerfile地址 -t 新镜像名字:TAG .
    

    注意最后有一个.,表示当前目录.

    本实验构建my-centos镜像的命令如下:

    docker build -f Dockerfile -t my-centos .
    

    输出如下

    Step 1/10 : FROM centos
     ---> 300e315adb2f
     
    Step 2/10 : MAINTAINER chenh<123@qq.com>
     ---> Running in c44ab3a18589
    Removing intermediate container c44ab3a18589
     ---> 7cd26ffc364d
     
    Step 3/10 : ENV MYPATH /root
     ---> Running in 1b0e6f4f4fa2
    Removing intermediate container 1b0e6f4f4fa2
     ---> 741fb6aecb01
    
    Step 4/10 : WORKDIR $MYPATH
     ---> Running in a2b56ea26c54
    Removing intermediate container a2b56ea26c54
     ---> 71e9bb4c397f
     
    Step 5/10 : RUN yum -y install vim
     ---> Running in a31932ed0c54
    CentOS Linux 8 - AppStream                      1.6 MB/s | 6.3 MB     00:03
    CentOS Linux 8 - BaseOS                         588 kB/s | 2.3 MB     00:03
    CentOS Linux 8 - Extras                         8.8 kB/s | 8.6 kB     00:00
    Dependencies resolved.
    Install  5 Packages
    Complete!
    Removing intermediate container a31932ed0c54
     ---> 5d1a36c2ee7a
     
    Step 6/10 : RUN yum -y install net-tools
     ---> Running in 60c7b23e18ec
    Last metadata expiration check: 0:00:14 ago on Thu Feb 18 11:52:47 2021.
    Dependencies resolved.
    Total download size: 322 k
    Installed size: 942 k
    Downloading Packages:
    Complete!
    Removing intermediate container 60c7b23e18ec
     ---> 7c09e53a2051
     
    Step 7/10 : EXPOSE 80
     ---> Running in 2f7481e6d0e0
    Removing intermediate container 2f7481e6d0e0
     ---> 3b2bb84afa87
    
    Step 8/10 : CMD echo $MYPATH
     ---> Running in 08a3a22956c2
    Removing intermediate container 08a3a22956c2
     ---> 6aef0d4c26df
    
    Step 9/10 : CMD echo "---end---"
     ---> Running in 9a21ec5aa63e
    Removing intermediate container 9a21ec5aa63e
     ---> 494ed5a122ad
    
    Step 10/10 : CMD /bin/bash
     ---> Running in 3ccac448e32d
    Removing intermediate container 3ccac448e32d
     ---> 7e2c3b0cc70e
    
    Successfully built 7e2c3b0cc70e
    
    Successfully tagged my-centos:latest
    

    可以看到Docker逐句解析并执行Dockerfile,每执行一次就生成一个新层.

  3. 运行容器:

    docker run -it my-centos
    

    运行该容器后,发现默认进入到/root目录下,且vimifconfig命令均能正常工作.

  4. 列出镜像变更历史:

    docker history my-centos
    

    可以看到镜像的变更历史如下:每一条变更历史对应一层

    IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
    7e2c3b0cc70e   6 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B
    494ed5a122ad   6 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B
    6aef0d4c26df   6 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B
    3b2bb84afa87   6 minutes ago   /bin/sh -c #(nop)  EXPOSE 80                    0B
    7c09e53a2051   6 minutes ago   /bin/sh -c yum -y install net-tools             23.3MB
    5d1a36c2ee7a   6 minutes ago   /bin/sh -c yum -y install vim                   58.1MB
    71e9bb4c397f   7 minutes ago   /bin/sh -c #(nop) WORKDIR /root                 0B
    741fb6aecb01   7 minutes ago   /bin/sh -c #(nop)  ENV MYPATH=/root             0B
    7cd26ffc364d   7 minutes ago   /bin/sh -c #(nop)  MAINTAINER chenh<123@qq.c…   0B
    300e315adb2f   2 months ago    /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
    <missing>      2 months ago    /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
    <missing>      2 months ago    /bin/sh -c #(nop) ADD file:bd7a2aed6ede423b7…   209MB
    
练习2:构建自定义Tomcat镜像
  1. 编写Dockerfile如下:

    FROM centos
    MAINTAINER chenh<123@qq.com>	
    
    #把java与tomcat添加到容器中
    ADD jdk-8u11-linux-x64.tar.gz /usr/local/
    ADD apache-tomcat-9.0.22.tar.gz /usr/local/
    
    #安装vim编辑器
    RUN yum -y install vim
    
    #设置工作访问时候的WORKDIR路径,登录落脚点
    ENV MYPATH /usr/local
    WORKDIR $MYPATH
    
    #配置java与tomcat环境变量
    ENV JAVA_HOME /usr/local/jdk1.8.0_11
    ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
    ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.22
    ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.22
    ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
    
    #容器运行时监听的端口
    EXPOSE 8080
    
    #启动时运行tomcat
    CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.22/bin/logs/catalina.out
    
  2. 构建镜像: docker build -t my-tomcat .

  3. 运行容器:

    docker run -d -p 9090:8080 --name my-tomcat \
    	-v /home/tomcat/test:/usr/local/apache-tomcat9.0.22/webapps/test \
    	-v /home/tomcat/tomcat9logs:/usr/local/apache-tomcat9.0.22/logs \
    	--privileged=true \		
        my-tomcat
    

    使用--privileged=true是为了避免因为权限报错

  4. 运行curl localhost:9090命令,得到结果如下:

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <title>Apache Tomcat/9.0.41</title>
            <link href="favicon.ico" rel="icon" type="image/x-icon" />
            <link href="tomcat.css" rel="stylesheet" type="text/css" />
        </head>
    
        <body>
            <div id="wrapper">
                <div id="navigation" class="curved container">
                    <span id="nav-home"><a href="https://tomcat.apache.org/">Home</a></span>
                    <span id="nav-hosts"><a href="/docs/">Documentation</a></span>
                    <span id="nav-config"><a href="/docs/config/">Configuration</a></span>
                    <span id="nav-examples"><a href="/examples/">Examples</a></span>
                    <span id="nav-wiki"><a href="https://wiki.apache.org/tomcat/FrontPage">Wiki</a></span>
                    <span id="nav-lists"><a href="https://tomcat.apache.org/lists.html">Mailing Lists</a></span>
                    <span id="nav-help"><a href="https://tomcat.apache.org/findhelp.html">Find Help</a></span>
                    <br class="separator" />
                </div>
            </div>
        	<!-- ... -->    
        </body>
    </html>
    
  5. 进入/home/tomcat/test目录下,发布一个程序:

    创建web.xml,内容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        id="WebApp_ID" version="2.5">
        <display-name>test</display-name>
    </web-app>
    

    创建a.jsp,内容如下:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>hello,kuangshen</title>
    </head>
        <body>
            -----------welcome------------
            <%=" my docker tomcat,kuangshen666 "%>
            <br>
            <br>
            <% System.out.println("-------my docker tomcat-------");%>
        </body>
    </html>
    
  6. 运行curl localhost:9090/test/a.jsp,得到结果如下:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>hello,kuangshen</title>
    </head>
        <body>
            -----------welcome------------
             my docker tomcat,kuangshen666 
            <br>
            <br>
            -------my docker tomcat-------
        </body>
    </html>
    

发布镜像

本节略

Docker网络

在研究Docker网络之前,先删除所有镜像和容器:

docker rm -f $(docker ps -a -q) 	# 删除所有容器
docker rmi -f $(docker images -qa) 	# 删除全部镜像

桥接网卡docker0

在宿主机中使用ip addr查看网卡信息如下:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
       
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:2f:80:19 brd ff:ff:ff:ff:ff:ff
    inet 172.21.16.4/20 brd 172.21.31.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe2f:8019/64 scope link
       valid_lft forever preferred_lft forever
       
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:aa:6b:53:81 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aaff:fe6b:5381/64 scope link
       valid_lft forever preferred_lft forever

可以看到宿主机有3块网卡,分别是回环网卡lo,物理网卡eth0和Docker桥接网卡docker0

  • 为了验证Docker容器之间是否能够通信,创建一个容器tomcat01并查询其网卡信息:

    docker run -d -P --name tomcat01 tomcat
    docker exec -it tomcat01 ip addr
    

    可以看到tomcat01容器的网卡信息如下:

    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
    
    136: eth0@if137: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
           valid_lft forever preferred_lft forever
    

    在宿主机中尝试ping容器tomcat01: ping 172.17.0.2,发现能够ping通:

    64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.047 ms
    64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.053 ms
    64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.055 ms
    64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.056 ms
    ...
    

    再次在宿主机使用ip addr命令查看网卡信息,发现宿主机多出了一块网卡veth2387536@if136:

    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        # ...
        
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        # ...
    
    3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    	# ...
    
    137: veth2387536@if136: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
        link/ether 52:05:09:20:5c:f9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet6 fe80::5005:9ff:fe20:5cf9/64 scope link
        	valid_lft forever preferred_lft forever
    

    我们发现宿主机的veth2387536@if136和容器内的eth0@if137组成了一对网卡.

  • 再启动一个容器tomcat02,发现宿主机内又多了一个和容器tomcat02内网卡相对应的网卡.

    docker run -d -P --name tomcat02 tomcat
    docker exec -it tomcat02 ip addr
    

    容器tomcat02内网卡信息如下:

    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
           
    138: eth0@if139: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
           valid_lft forever preferred_lft forever
    

    宿主机内网卡信息如下:

    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        # ...
        
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        # ...
        
    3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        # ...
        
    137: veth2387536@if136: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
        link/ether 52:05:09:20:5c:f9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet6 fe80::5005:9ff:fe20:5cf9/64 scope link
           valid_lft forever preferred_lft forever
           
    139: veth2bed2bf@if138: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
        link/ether 96:90:58:5a:80:62 brd ff:ff:ff:ff:ff:ff link-netnsid 1
        inet6 fe80::9490:58ff:fe5a:8062/64 scope link
           valid_lft forever preferred_lft forever
    
  • 下面测试宿主机与容器之间能否ping通:

    • 在宿主机内通过IP地址ping容器tomcat01,tomcat02,能够ping通.

      ping 172.17.0.2		# 在宿主机内ping容器tomcat01的IP地址,能ping通
      ping 172.17.0.3		# 在宿主机内ping容器tomcat02的IP地址,能ping通
      
    • 在容器tomcat01内通过IP地址ping宿主机和容器tomcat02,能够ping通.

      docker exec -it tomcat01 ping 172.17.0.1	# 在容器tomcat01内ping宿主机的IP地址,能ping通
      docker exec -it tomcat01 ping 172.17.0.3	# 在容器tomcat01内ping容器tomcat02的IP地址,能ping通
      
    • 在容器tomcat02内通过IP地址ping宿主机和容器tomcat01,能够ping通.

      docker exec -it tomcat02 ping 172.17.0.1	# 在容器tomcat02内ping宿主机的IP地址,能ping通
      docker exec -it tomcat02 ping 172.17.0.1	# 在容器tomcat02内ping容器tomcat01的IP地址,能ping通
      

结论: Docker在宿主机内安装的虚拟网卡docker0是一个桥接网卡,使用了veth-pair技术.

在这里插入图片描述

--link

使用docker0作为桥接网卡可以实现容器间的通信,但是这种方法存在一个局限: 容器间只能通过IP地址进行通信,不能通过容器名或容器id进行通信.

ping 172.17.0.2		# 在宿主机内ping容器tomcat01的IP地址,能ping通
ping tomcat01		# 在宿主机内ping容器tomcat01的容器名,不能ping通

docker exec -it tomcat01 ping 172.17.0.3	# 在容器tomcat01内ping容器tomcat02的IP地址,能ping通
docker exec -it tomcat01 ping tomcat02		# 在容器tomcat01内ping容器tomcat02的容器名,不能ping通

通过在启动容器时添加--link 要连接的容器名或id参数,可以实现通过容器名或id进行通信,示例如下:

docker run -d -P --name tomcat03 --link tomcat02 tomcat 

创建容器tomcat03连接到容器tomcat02中,这样就可以在容器tomcat03中t那个锅容器名或容器id来ping容器tomcat02

docker exec -it tomcat03 ping 172.17.0.3	# 在容器tomcat03内ping容器tomcat02的IP地址,能ping通
docker exec -it tomcat03 ping tomcat02		# 在容器tomcat03内ping容器tomcat02的容器名,能ping通
docker exec -it tomcat03 ping 94cd188b01ea	# 在容器tomcat03内ping容器tomcat02的容器id,能ping通

--link机制是通过修改/etc/hosts文件实现的,进入容器tomcat03查看其/etc/hosts文件: docker exec -it tomcat03 cat /etc/hosts如下:

127.0.0.1   localhost
::1     	localhost ip6-localhost ip6-loopback
fe00::0 	ip6-localnet
ff00::0 	ip6-mcastprefix
ff02::1 	ip6-allnodes
ff02::2 	ip6-allrouters
172.17.0.3  tomcat02 94cd188b01ea	# 容器tomcat02的容器名和容器id
172.17.0.4  4ea43009c443			# 容器tomcat03自身的容器id

因为--link是通过修改hosts文件实现连接的,因此不能实现双向连接,不推荐使用.

自定义网络

使用docker network命令可以对Docker网络进行操作.

首先使用docker network ls命令列出所有网络,输出如下:

NETWORK ID     NAME      DRIVER    SCOPE
2c2577c5c04e   bridge    bridge    local
8a11f7f5238a   host      host      local
c77811056092   none      null      local

其中DRIVER属性表示网络模式,具体如下:

网络模式docker run命令中的参数意义
bridge--net=bridge,是docker run命令的默认参数在Docker网桥docker0上为容器创建新的网络栈
none--net=none不配置网络,用户可以稍后进入容器,自行配置
container--net=container:name/id和另外一个容器共享Network namespace
host--net=host容器和宿主机共享Network namespace
用户自定义--net=自定义网络名用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络

使用docker run命令创建容器时,默认参数是--net=bridge,即通过docker0网桥组网.


使用docker network inspect可以查看网络的详细信息:

docker network inspect bridge

输出如下,可以在Containers节点中看到该网络下有我们之前创建的tomcat01,tomcat02,tomcat033个容器:

[
    {
        "Name": "bridge",
        "Id": "2c2577c5c04e67dcff83b9509b44d249f6b8f28391550a9835d840f2f1349a79",
        "Created": "2021-02-17T23:23:27.045956359+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {		// 我们之前创建的3个容器
            "3c35894e46aedb420bf560a9db4f393012a999ba99dc5679077ee3ac8113b7b4": {
                "Name": "tomcat01",
                "EndpointID": "739cc96286e2bb55526b68ff0a9eab3aa65a8c09a9e524abb7d8f3630c6f5aec",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            },
            "4ea43009c443efb3ba5fb0c247d63904c252092e1055e67bb70f140d35a38ee9": {
                "Name": "tomcat03",
                "EndpointID": "6eaad11d71a7852e99d0ad04c2feda731e69744f273f541c98e176fc512bbdeb",
                "MacAddress": "02:42:ac:11:00:04",
                "IPv4Address": "172.17.0.4/16",
                "IPv6Address": ""
            },
            "94cd188b01ea1bb3472a7c192c9edf4e322da03691baa99080ddede880fba7bf": {
                "Name": "tomcat02",
                "EndpointID": "d9f7a82132ea3d467eba5434a7b5912b667f7670b331dc59f0237c96dd4730bf",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

可以使用docker network create命令创建自定义网络:

docker network create \
	--driver bridge \			
	--subnet 192.168.0.0/16 \
	--gateway 192.168.0.1 \
	mynet

在这里创建了自定义网络mynet,网络模式为桥接模式,网段为192.168.0.0/16,网关为192.168.0.0/16

在该网络下创建2个容器tomcat-mynet01tomcat-mynet02:

docker run -d -P --name tomcat-mynet01 --net mynet tomcat
docker run -d -P --name tomcat-mynet02 --net mynet tomcat

在自定义网络内,可以使用容器名或容器id访问网络内的其它容器.

docker exec -it tomcat-mynet01 ping 192.168.0.3		# 在容器tomcat-mynet01内ping容器tomcat-mynet02的IP地址,能ping通
docker exec -it tomcat-mynet01 ping tomcat-mynet02	# 在容器tomcat-mynet01内ping容器tomcat-mynet02的容器名,能ping通
docker exec -it tomcat-mynet01 ping 13251b560879	# 在容器tomcat-mynet01内ping容器tomcat-mynet02的容器id,能ping通

docker exec -it tomcat-mynet02 ping 192.168.0.2		# 在容器tomcat-mynet02内ping容器tomcat-mynet01的IP地址,能ping通
docker exec -it tomcat-mynet02 ping tomcat-mynet01	# 在容器tomcat-mynet02内ping容器tomcat-mynet01的容器名,能ping通
docker exec -it tomcat-mynet02 ping da45a19c956e	# 在容器tomcat-mynet02内ping容器tomcat-mynet01的容器id,能ping通

Docker的不同网络之间默认是隔离的,可以使用docker network connect命令将容器和网络连通,格式如下:

docker network connect [OPTIONS] NETWORK CONTAINER
  • 上文创建的mynet网络和默认的docker0网络是隔离的,两个网络之间的容器不能互相访问

    docker exec -it tomcat01 ping 192.168.0.3		# 在容器tomcat01内ping容器tomcat-mynet01的IP地址,不能ping通
    docker exec -it tomcat01 ping tomcat-mynet01	# 在容器tomcat01内ping容器tomcat-mynet01的容器名,不能ping通
    docker exec -it tomcat01 ping da45a19c956e		# 在容器tomcat01内ping容器tomcat-mynet01的容器id,不能ping通
    
  • 使用docker network connect命令将tomcat01连接到mynet网络上

    docker network connect mynet tomcat01
    

    使用docker network inspect mynet命令查看mynet网络的详细信息,可以看到mynet网络下包含了tomcat01容器.

    [
        {
            "Name": "mynet",
            "Id": "203a1e68d66172078f437d23f146dd1309592dcd6eab87a7d4461aea119bdb08",
            // ...
            "Containers": {	// 可以看到容器中除了原有的tomcat-mynet01和tomcat-mynet02容器之外,又增加了tomcat01容器
                "da45a19c956e881c4c445e49b301f2bcbdf0e388bc29925da080dfab19560b21": {
                    "Name": "tomcat-mynet01",
                    "EndpointID": "da7a5a3a5e424b63359ec13637743a97359bb5ba5461aacab115f14687001bed",
                    "MacAddress": "02:42:c0:a8:00:02",
                    "IPv4Address": "192.168.0.2/16",
                    "IPv6Address": ""
                },
                "13251b5608793d7fd4ee16aae520fb9d45846f122da351438eaac0565591c438": {
                    "Name": "tomcat-mynet02",
                    "EndpointID": "f78746e31a9d691625b271ee4ebc63774bac1a8c93956e894e494b175cbcf19a",
                    "MacAddress": "02:42:c0:a8:00:03",
                    "IPv4Address": "192.168.0.3/16",
                    "IPv6Address": ""
                },
                "3c35894e46aedb420bf560a9db4f393012a999ba99dc5679077ee3ac8113b7b4": {
                    "Name": "tomcat01",
                    "EndpointID": "e5d23183e8ad0454c8e8de7434f6480ee304dfeb2556b64bf7b8c7591a7983bb",
                    "MacAddress": "02:42:c0:a8:00:04",
                    "IPv4Address": "192.168.0.4/16",
                    "IPv6Address": ""
                },
            },
            // ...
        }
    ]
    
    

    查看容器tomcat01的IP地址可以看到,容器tomcat01有两个IP地址: docker exec -it tomcat01 ip addr

    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
    
    136: eth0@if137: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
           valid_lft forever preferred_lft forever
    
    148: eth1@if149: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:c0:a8:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 192.168.0.4/16 brd 192.168.255.255 scope global eth1
           valid_lft forever preferred_lft forever
    
  • 容器tomcat01可以和mynet下的容器进行通信了:

    docker exec -it tomcat01 ping 192.168.0.3		# 在容器tomcat01内ping容器tomcat-mynet01的IP地址,不能ping通
    docker exec -it tomcat01 ping tomcat-mynet01	# 在容器tomcat01内ping容器tomcat-mynet01的容器名,不能ping通
    docker exec -it tomcat01 ping da45a19c956e		# 在容器tomcat01内ping容器tomcat-mynet01的容器id,不能ping通
    

在这里插入图片描述

练习1: 部署一个Redis集群

  1. 创建redis网络:

    docker network create redis --subnet 172.38.0.0/16
    
  2. 启动6个Redis容器,这里使用脚本生成命令并运行:

    创建6个Redis节点的配置文件

    # 通过脚本创建六个redis配置
    for port in $(seq 1 6); do
    mkdir -p /mydata/redis/node-${port}/conf
    touch /mydata/redis/node-${port}/conf/redis.conf
    cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
    port 6379
    bind 0.0.0.0
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 5000
    cluster-announce-ip 172.38.0.1${port}
    cluster-announce-port 6379
    cluster-announce-bus-port 16379
    appendonly yes
    EOF
    done
    

    创建6个Redis镜像

    for port in $(seq 1 6); do
      docker run \
      -p 637"${port}":6379 -p 1637"${port}":16379 \
      --name redis-"${port}" \
      -v /mydata/redis/node-"${port}"/data:/data \
      -v /mydata/redis/node-"${port}"/conf/redis.conf:/etc/redis/redis.conf \
      -d \
      --net redis --ip 172.38.0.1"${port}" \
      redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf;
    done
    
  3. 进入其中一个容器,创建集群

    # 进入容器redis-1
    docker exec -it redis-1 /bin/bash
    
    # 创建集群
    redis-cli --cluster create \
    	172.38.0.11:6379 \
    	172.38.0.12:6379 \
    	172.38.0.13:6379 \
    	172.38.0.14:6379 \
    	172.38.0.15:6379 \
    	172.38.0.16:6379 \
    	--cluster-replicas 1
    
  4. 查看集群信息

    # 连接集群
    redis-cli -c
    
    # 查看集群信息
    cluster info
    
    # 查看节点
    cluster nodes
    

练习2: 将SpringBoot项目打包成Docker镜像

  1. 使用IDEA创建一个SpringBoot项目:

    创建项目后创建helloController.java文件如下:

    @RestController
    public class HelloController {
    
        @GetMapping("/hello")
        public String hello(){
        	return "hello world";
        }
    }
    

    使用MAVEN将项目打包成jar包app.jar

  2. 在项目下编写Dockerfile如下:

    FROM java:8
    
    # 服务器只有dockerfile和jar在同级目录
    COPY *.jar /app.jar
    CMD ["--server.port=8080"]
    
    # 指定容器内要暴露的端口
    EXPOSE 8080
    ENTRYPOINT ["java","-jar","/app.jar"]
    
  3. 将Dockerfile和项目的jar包上传到linux服务器上,构建镜像并启动容器:

    # 构建镜像
    docker build -t my-app .
    
    # 启动容器
    docker run -d -p 8080:8080 --name my-app my-app
    
  4. 测试访问:

    curl localhost:32779/hello
    

    输出:

    hello world
    

pdf版本笔记的下载地址: Docker学习笔记01(访问密码:3834)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值