docker入门不完全指南,这玩艺儿不用则废!

2023-07-0910:54:20服务器及运维Comments873 views字数 17484阅读模式

简介

简单地说,docker是一个开放平台,你可以基于它开发、迁移代码以及运行应用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker提供了在独立的环境中打包(package)和运行应用的能力,而这种松散(loosely)独立的环境我们一般把它叫做容器(container)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

有了容器,我们可以将我们项目所需要的环境全部一起打包,然后在另一个带有docker的环境比如云服务器上直接安装镜像运行容器,而这个过程我们不需要再去做额外的比如环境搭建之类的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们的容器中已经自带了环境了。而且我们可以同时运行多个不同环境的容器,也不用担心它们之间会有环境污染,因为容器之间是相互独立的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

另外dockerCI/CDcontinuous integration and continuous delivery)工作流也很搭。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


docker的架构

docker使用的是client-server的架构, 客户端也就是提供给用户api的工具(docker compose[3] 也是一个客户端,用于管理多个容器),它会和docker daemon相当于中间层沟通(后面就叫守卫或者dockerd了),它侧重于构建、运行以及分发容器,中间层可以是同个系统的,也可以是远程的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

客户端和守卫之间是通过基于UNIX socket或者网络接口的REST API来联系的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_client_server

docker daemon

docker守卫(dockerd)会监听请求以及管理docker对象,比如镜像(images)、容器(containers) 、网络以及卷积(volumes)。它也能够和其它守卫建立联系来管理docker服务。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


docker client

客户端(docker)是用户主要用来和docker交互的方式,我们输入指令,它会发送给dockerd,然后dockerd会执行它。客户端可以和多个dockerd建立联系。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


Docker Desktop

一个可视化应用,支持win/mac/linux三个操作系统,我们可以可视化的创建、构建容器应用和微服务(microservices)。它包含了以下这些内容:Docker daemon (dockerd)the Docker client (docker)Docker ComposeDocker Content TrustKubernetes, and Credential Helper文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


docker registries

docker有一个注册表用来存储镜像。Docker Hub是一个公共的注册表,任何人都可以从上面下载镜像。而客户端默认也是从这里面下载镜像。当然,你也可以搞一个私有注册表用来放你自己的镜像。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


docker objects

前面提到过的镜像、容器、网络、卷积等都是docker对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


镜像

镜像是一个只读的模板,它提供了创建容器的api。一般情况下,一个镜像是在另一个镜像的基础上自定义的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

比如我们创建的镜像可能是在ubuntu的基础上构建的,不过在这个基础上,我们加了自定义的内容,比如Apacheweb服务器以及我们的应用程序,当然还有相关的配置信息。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

后面我们会学习到通过Dockerfile批量执行docker指令,然后会在镜像之上创建一个layer,当我们重新构建镜像的时候只会去构建这个layer文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


容器

容器是可运行的镜像实例,你可以创建、开始、停止、移除容器。另外我们还可以通过网络来获取容器里的数据,甚至是基于当前容器里的数据再创建一个镜像。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

默认情况下,容器是独立的,各容器之间不会有联系。我们可以通过暴露出来的网络端口/数据或者其他容器/主机的底层子系统来操作容器。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

容器的定义取决于镜像以及你创建/开启容器时设置的配置项。当容器被移除,任何改变state的数据都将不会被存储。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们可以用docker run指令创建一个基于某个镜像的容器。比如我们基于ubuntu镜像来创建一个容器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker run -i -t ubuntu /bin/bash

这里假设我们用的是默认的配置项。当运行完这条指令之后,一个基于ubuntu镜像的容器就创建好了(你可以通过docker ps -a来查看容器是否创建)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这一条指令执行过程中涉及到了如下几个几步:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

  1. 如果你本地没有ubuntu镜像,那么Docker就会自动帮你从docker registries上面pull下来,相当于你手动docker pull ubuntu
  2. docker创建一个新的容器,相当于你手动docker container create
  3. docker分配一个读写文件系统(read-write filesystem)给这个新容器,作为它最终的layer。这一步允许了我们的容器创建或者修改本地文件系统的文件和文件夹。
  4. docker给容器创建了一个网络接口,这样就能和默认的网络连接上。这里面包含了给容器分配IP地址。默认情况下,容器可以通过主机的网络连接来连接额外的网络。
  5. docker开启容器和调用/bin/bash执行容器。我们这里用到了-i-t两个标志位,所以容器是可通过终端交互的,你可以通过输入关键字来和容器交互,交互的log会被输出在终端。
  6. 我们可以通过输入exit(一般ctrl + c也行)来终止交互,这样容器就停止了(注意没有移除)。

底层技术

docker是使用go语言编写的并且使用linux内核(kernel)的一些特性来提供功能。docker使用了一种叫namespaces的技术来提供独立的工作空间,即容器。当运行一个容器的时候,docker会给这个容器创建一系列命名空间。而这些命名空间提供了一个独立的layer。容器的每一部分都运行在一个单独的命名空间并且它的访问受限于这个命名空间。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


安装

咱本机可以选择安装可视化工具docker desktop文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我这本机是window,所以接下来操作都基于window下的docker desktop文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

由于docker底层使用到了linux内核的部分特性,所以我们并不能直接在window上安装docker文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

window上安装linux

官方文档:安装 WSL | Microsoft Learn文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

另外可以搭配:Windows 11 安装 WSL2 - 知乎 (zhihu.com)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这一点如果你在window上安装过redis应该就知道如何处理了。win10在某个版本之后就支持了将linux系统作为子系统,也就是WSL文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

系统版本要求文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_required

满足先决条件之后打开控制面板的程序的启用或关闭Windows功能文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_open_setting
docker入门不完全指南,这玩艺儿不用则废!
img_change_setting

勾上这几个项,我的电脑是win11的,没有Hyper-V,也不影响。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

注意这个虚拟机平台必须勾上,不然可能会遇到无法运行ubuntu应用的问题。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

接着打开mcrosoft store,搜索ubuntu,选择评分最高那个文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_download_ubuntu

当然你也可以按传统的方式下载ubuntu文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

wsl --install -d Ubuntu 

安装完之后还不能直接打开,由于当前内核的版本并不是最新的,我们还需要升级下版本文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

通过管理员方式打开powershell,然后输入文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

bcdedit /set hypervisorlaunchtype auto
wsl --update 

稳妥点重启下电脑文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后打开这个ubuntu应用,最开始会让你设置用户名和密码,root并不能直接使用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_open_success

这样就表示在window上安装linux成功了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后你也可以用hostnamectl看下当前版本。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


docker desktop

回到我们最开始的点,我们是想安装docker desktop文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

直接到这点击下载应用:Install Docker Desktop on Windows | Docker Documentation文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后安装即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_docker_desktop

docker engine(Ubuntu)

当然,你也可以在之前安装的ubuntu应用打开后通过命令行的方式去安装docker:Install Docker Engine on Ubuntu | Docker Documentation文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

然后测试下是否正常安装文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

sudo docker run hello-world

如果你之前安装过了,你可能需要在安装之前先移除旧版本文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

另外移除docker并不会移除原来的容器等,所以如果你不想要之前的数据,你可以执行以下操作文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd 

其中/var/lib/docker存放的是镜像、容器、卷积和网络等。具体可见:Install Docker Engine on Ubuntu | Docker Documentation文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


get started

接下来我们将通过以下的几步来了解和学习docker的用法:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

  • 构建镜像和运行镜像的容器
  • 使用docker hub分享你的镜像
  • 使用具有数据库的多个容器部署 Docker 应用程序
  • 使用Docker Compose运行程序

不过在开始之前,我们需要再加深对容器和镜像概念的理解。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

什么是容器?

容器实际上是一个在你主机上独立、有别于其它进程的沙盒(sandboxed)进程。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

如何实现的独立,前面说过是基于linux内核的namespace技术,具体可见:Demystifying Containers - Part I: Kernel Space | by Sascha Grunert | Medium文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

总之,容器具有以下特性:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

  • 是镜像的可运行实例,你可以使用create、start、stop以及delete这几个api来操作容器
  • 可以运行在宿主机、虚拟机和云端部署
  • 便携式的(portable),可以运行在所有OS
  • 独立于其它容器,运行它自己软件、二进制(binaries)以及配置

什么是镜像?

前面我们知道了容器是运行在镜像之上的文件系统,而容器又可以跑你的应用。那么作为容器的基础:镜像自然就需要包含所有容器需要的东西,比如依赖、配置等。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们后面会深入了解到镜像。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

(我怀疑简介和这一章不是同一个老哥写的)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


打包(containerize)一个应用镜像

接下来我们将搞一个web前端应用,基础环境自然就是nodeJS的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

什么?你没用过nodejs?请出门左拐:Node.js文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

噢,你先别拐,咱这只是将它作为环境依赖安装下,并不会用到里面的api等。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

先在本机创建一个docker-study的文件夹, 然后拉个官方给的demo文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

mkdir docker-study
cd docker-study
git clone https://github.com/docker/getting-started.git

这个git分支貌似需要开魔法才能拉下来。。。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_menu

这应该是一个workspaces。我们的目标在app文件夹里。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

项目里的代码我们不用去探究了解,我们的目的是知道如何打包和运行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们在app根目录创建一个Dockerfile文件。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

t# syntax=docker/dockerfile:1
   
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000

这里面几行是在干啥我们暂时不分析,现在我们可以开始构建镜像了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker build -t getting-started .
  • docker build:构建容器镜像的指令
  • -ttag,也就是你镜像的名字
  • .:这最后一个点表示需要在当前目录下查找Dockerfile
docker入门不完全指南,这玩艺儿不用则废!
img_building
docker入门不完全指南,这玩艺儿不用则废!
img_build_success

这时你的docker desktop里应该就会看到我们刚打包的镜像文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_images

打包的过程中可能下载了一堆的layer,这是因为我们前面在DockerfileFROM node:18-alpine,我们需要nodejs环境,如果你电脑没有,就会先下载对应的镜像。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们回过头来分析下Dockerfile里的内容:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

  • FROM[13] node:18-alpine:我们希望基于node:18-alpine这个镜像上开始构建镜像。
FROM [--platform=<platform>] <image> [AS <name>]
// or
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
// or
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
  • WORKDIR[14] /app:工作目录,其余指令比如RUN/CMD等的执行目录,可以指定多个,下面这样的相当于/a/b/c
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
  • COPY [15]. .:复制新文件(夹)到目标目录,目标目录作为容器的文件系统目录
COPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]
  • RUN[16] yarn install --production: 执行yarn install指令,--production是参数。这个api执行时会在最顶层创建一个layer,然后把执行的结果提交出去给下一步指令。
RUN <command> // (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows)
RUN ["executable", "param1", "param2"] // (exec form)
  • CMD ["node", "src/index.js"]:调用node运行src/index.js文件。 一个Dockerfile只能有一个CMD,如果你写了多个则按最后一个为准。 你可能觉得这个CMDRUN作用有些类似,实际上两者做的事情并不一样,CMD是作为执行容器的默认值,而RUN则是容器执行前的一系列操作。 你也可以用ENTRYPOINT进行覆盖,格式均为JSON数组。
CMD ["executable","param1","param2"] // (exec form, this is the preferred form)
CMD ["param1","param2"] // (as default parameters to ENTRYPOINT)
CMD command param1 param2 // (shell form)
  • EXPOSE[17] 3000: 容器监听的端口,默认是TCP的,也可以设置成UDP。 实际上这里并没有真正的暴露端口,而是相当于文档类型一样。如果有着需要,docker run的时候你可以带上-p参数。
EXPOSE <port> [<port>/<protocol>...]
//example
EXPOSE 80/tcp
EXPOSE 80/udp

比如:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker run -p 80:80/tcp -p 80:80/udp ...

这个例子如果带上-p,会同时创建两个端口,一个tcp和一个udp的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


运行容器[18]

现在我们已经有了镜像,要创建并运行容器只需要简单的一个指令。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker run -dp 127.0.0.1:3000:3000 getting-started
  • -dp:是-d-p的简写
  • -p:前面说过了,是--publish的缩写
  • -d:是--detached的缩写,这样你的容器就可以在后台运行
  • 127.0.0.1:3000:3000:格式为HOST: CONTAINER。 127.0.0.1:3000Host,表示主机的地址加端口,这个端口你可以用来暴露到公网等,而后面的3000则表示容器监听的端口,这俩端口不必一致,实际上是做了一层映射。
  • getting-started:我们的镜像名字
docker入门不完全指南,这玩艺儿不用则废!
img_run_container
docker入门不完全指南,这玩艺儿不用则废!
img_containers

然后我们直接访问localhost:3000文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_connect_to_localhost_3000

这样我们的容器就在后台跑起来了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

下面我们来更新下我们的应用文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


更新应用[19]

首先我们来修改下项目的代码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

找到src/static/js/app.js文件文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

- <p className="text-center">No items yet! Add one above!</p>
+ <p className="text-center">You have no todo items yet! Add one above!</p> 

然后回到app文件夹里,我们需要重新构建一次镜像。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker build -t getting-started . 

构建完之后先暂时不要执行运行容器的指令,由于镜像已经被占用了,所以这个时候你运行容器可能会失败,因为当前镜像已经有一个运行中的容器了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们需要先删除对应的容器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker-desktop中直接找到container那一栏删掉对应的容器即可,命令行的如下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker stop <container-id> // <container-name> 
docker rm <container-id> // <container-name> 
docker入门不完全指南,这玩艺儿不用则废!
img_remove_container

移除之前需要先停止容器运行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后再重新执行容器运行的指令文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker run -dp 127.0.0.1:3000:3000 getting-started
docker入门不完全指南,这玩艺儿不用则废!
img_update_container_success

这样就完成了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


分享应用[20]

我们可以把我们的app上传到docker hub上,这样别人或者自己的另一台机器上就可以下载你这个app了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

在我们上传自己的app之前,我们需要先注册一个docker账号:docker.com/pricing?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后来这创建一个仓库:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_create_respositories

注意要选择public,这样别人才能pull的到你的镜像。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_create_public_image

当然,我们一般都是自己用的(要钱)。。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

  • name:是你发布的镜像的名字

设置完了点击create文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

创建好了屏幕右边可以看到一个指令提示文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker push [yourname]/[respository]:[tag]
  • tag:这个指的是版本,默认是latest
  • respository:这个要和你的镜像名字对应上
  • yourname:这个是你的用户名,记得先登录docker desktop,如果你是用命令行的,你需要使用一下指令进行登录
docker login -u YOUR-USER-NAME

我们回到我们的项目中执行如下指令文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker push [yourname]/[respository]

然后你会发现报错了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
image_not_existed

没有对应的镜像文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这个时候我们就需要用到tag[21]修改已有镜像的名字文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker tag getting-started YOUR-USER-NAME/getting-started 
docker入门不完全指南,这玩艺儿不用则废!
img_rename_image

然后我们再重新执行push指令文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

但这里我们又遇到另一个问题文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_requested_access_is_denied

这个问题是因为我们先用docker desktop登陆了,所以我们现在需要先logout,然后重新登录文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker logout

docker login -u "mazeyqian" -p "Password" docker.io

docker push [yourname]/getting-started
docker入门不完全指南,这玩艺儿不用则废!
img_push_success
docker入门不完全指南,这玩艺儿不用则废!
img_push_success

这样就发布成功了,我们在本机也可以用docker search的方式查看文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker search [yourname]/getting-started 
docker入门不完全指南,这玩艺儿不用则废!
img_search_image

然后我们可以将它拉下来运行,如果你有另一台机器,你可以在那台机器上试下,或者来到官方提供的线上平台试下:Play with Docker文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

流程和之前一样的,我这里就不演示了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


持久化数据库[22]

目前我们的数据是非持久性的,每次重新创建容器之后数据就都没了,前面说过容器之间是独立的(即使是基于同一个镜像的容器数据也不共享),所以默认数据是不会出现在容器外的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们现在来将数据同步到本机来实现持久化。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

项目中使用的数据库是SQLite[23] , 默认数据是存储在/etc/todos/todo.db里面,所以如果我们有个东西把里的数据包裹起来放到本机,那么即使镜像都没了我们的数据也不会丢失。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这个时候就需要用到挂载(mount)卷积(valumes[24])了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker volume create todo-db

docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started
  • --mount:用于指定要挂载到的对应卷积
  • /etc/todos:卷积挂载的对象,在容器里。

这样数据就可以持久了,我们可以重新创建容器和运行,然后随便搞点数据之后再移除容器,然后再创建一个容器并运行,这个时候你就会看到之前的数据还保留着文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_persist_data

现在我们的数据就被保留在卷积中了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们可以通过docker volume inspect指令去查看卷积存放的位置,确保数据是有持久化的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker volume inspect todo-db
[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
] 

(在公司,没有配置环境,所以直接用官方给的例子了。。)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

  • Mountpoint:磁盘存放的位置,也就是我们持久化的数据所在位置。

使用bind的方式挂载[25]

实际上我们还可以自定义存放的位置,通过bind的方式将主机本机的文件夹绑定到容器里,当分享的文件夹内容发生变化的时候会立即同步容器里的数据。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们先来看下这两种方式的区别文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

具名卷积(named volumes)绑定挂载(Bind mounts)
主机位置(Host location)由docker选择开发者设备
挂载例子 (using --mount)type=volume,src=my-volume,target=/usr/local/datatype=bind,src=/path/to/data,target=/usr/local/data
使用容器内容填充新的卷积
支持卷驱动程序

回到我们的app文件夹中,我们来试下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker run -it --mount type=bind,src="$(pwd)",target=/src ubuntu bash // linux/unix
docker run -it --mount "type=bind,src=$pwd,target=/src" ubuntu bash // windows powerShell
  • --mount:通知docker我这里是使用的绑定挂载的方式
  • -it:会创建一个伪的可交互终端,我们这就是一个ubuntu的容器终端
  • src:当前工作文件夹
  • target:容器里你想绑定的文件夹

执行了指令之后我们会直接进入容器的文件系统文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_ubuntu_terminal

然后我们进入src文件夹中,创建一个test.txt文件,这个时候你就会看到这个文件在我们的设备上也是同时生成的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_create_test_file

然后我们在设备上直接删除这个test.txt文件再在终端输入ls文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这个时候就看不到之前创建的test.txt文件了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_delete_test.txt

这么一看我们的文件夹确实绑定好了容器src文件夹了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

终结终端使用Ctrl + D文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这种方式下有些东西比如工具之类的我们并不需要单独去安装,都是会同步的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们再来将我们的app做一波这个流程,不过在这之前,我们需要先docker ps -a看下是否存在运行中的getting-started容器,如果有,先移除。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

// linux/unix
docker run -dp 127.0.0.1:3000:3000 \
    -w /app --mount type=bind,src="$(pwd)",target=/app \
    node:18-alpine \
    sh -c "yarn install && yarn run dev"

// windows
docker run -dp 127.0.0.1:3000:3000 `
    -w /app --mount "type=bind,src=$pwd,target=/app" `
    node:18-alpine `
    sh -c "yarn install && yarn run dev"
  • -dp:前面说过了,就是-d-p的简写
  • -w:指定工作目录,这里指定的是app文件夹作为执行后续指令的文件夹
  • node:18-alpine:不多说,就是基于node镜像搭建容器
  • sh -c "yarn install && yarn run dev":调用sh来执行安装依赖和运行的指令,alpine没有bash的。
  • nodemon[26]: dev这个指令执行的是nodemon src/index.js。这个nodemon是一个node工具,可以在程序内容发生变化的时候自动重新运行程序。

然后我们可以docker ps -a看下容器是否正常运行文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_ps_a

或者直接浏览器或者curl访问localhost:3000看是否正常。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

如果没正常运行起来,我们可以通过docker logs -f <container-id>的方式来查看错误信息文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_log_container

可以看到我这里是正常的log文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们随便找个文件getting-started\app\src\static\js\app.js修改内容,注意要在本机修改文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_edit

然后直接回到之前打开的localhost:3000,刷新下,这个时候按钮的文案就变了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_update_page_success

当我们开发完毕之后,我们直接删掉这个容器,打包镜像发布就完事了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker build -t getting-started .

当然,这个nodemon并不能做到热更新的效果,如果真要开发直接用webpack/vite即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


多容器应用[27]

前面我们的项目里涉及到的东西都是直接放到同一个容器里的,比如数据库数据等,而实际上应该把他们拆开,尽量做到专注于某部分,因为如果不同技术涉及到的环境、环境变量、工具等会使得这容器变得复杂,维护成本高。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

现在我们再搞个MySQL的容器来运行我们的数据库。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_multi_containers

不过这里还有个问题,那就是网络。由于容器之间相互独立隔离,它们并不知道彼此,所以也不能直接联系。这个时候我们可以使用network,如果这俩都在同一个网络下,那么它们就可以做到相互联系。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker network create todo-app

创建一个叫做todo-app的网络文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们创建一个MySQL容器并指向这个网络文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

// linux/unix
docker run -d \
     --network todo-app --network-alias mysql \
     -v todo-mysql-data:/var/lib/mysql \
     -e MYSQL_ROOT_PASSWORD=secret \
     -e MYSQL_DATABASE=todos \
     mysql:8.0 

// windows
docker run -d `
     --network todo-app --network-alias mysql `
     -v todo-mysql-data:/var/lib/mysql `
     -e MYSQL_ROOT_PASSWORD=secret `
     -e MYSQL_DATABASE=todos `
     mysql:8.0
  • -v:是--volume的简写
docker入门不完全指南,这玩艺儿不用则废!
  • -e:设置环境变量
  • MYSQL_ROOT_PASSWORDMySQL的密码
  • MYSQL_DATABASE:指定的数据库,环境变量相关的具体可见:MySQL Docker Hub listing.
  • --network-alias: 搞个别名,这样比较好分辨,虽然还是同一个network
docker入门不完全指南,这玩艺儿不用则废!
img_network-alias

这里我们并没有使用docker volume create,而是直接 todo-mysql-data:/var/lib/mysql,这里是通知docker我们想创建一个todo-mysql-data名字的卷积,如果没有这个卷积会自动创建并挂载/var/lib/mysql这个数据。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_auto_create_volumes

接下来我们来测试下是否正常创建容器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker exec -it <mysql-container-id> mysql -u root -p
  • exec:在一个运行中的容器中执行指令,这里的指令是mysql -u root -p
docker入门不完全指南,这玩艺儿不用则废!
img_exec

默认用的bash,所以可以不写。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_databases

可以看到我们前面设置的,这会儿已经有了todos数据库。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后exit可以退出文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们准备在另一个容器中连接这个数据库,每一个容器都有自己的ip地址,所以在同一个网络里我们可以通过ip去定位容器。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们先用nicolaka/netshoot来试下,这个镜像里面包含着很多的网络工具文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker run -it --network todo-app nicolaka/netshoot

// 然后在新终端中输入
dig mysql
docker入门不完全指南,这玩艺儿不用则废!
img_ip

dig命令是一个DNS工具。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这个172.18.0.2就是我们的网络,每个人的都不同。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这个mysql是容器的网络别名,我们前面用--network-alias定义的,到时候docker会解析这个mysql编程正确的ip地址。我们后面连接的时候直接用这个mysql别名即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这个app的项目中支持了几个环境变量可以直接接入mysql,所以我们直接配置下即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

// linux/unix
docker run -dp 127.0.0.1:3000:3000 \
   -w /app -v "$(pwd):/app" \
   --network todo-app \
   -e MYSQL_HOST=mysql \
   -e MYSQL_USER=root \
   -e MYSQL_PASSWORD=secret \
   -e MYSQL_DB=todos \
   node:18-alpine \
   sh -c "yarn install && yarn run dev"

// windows
docker run -dp 127.0.0.1:3000:3000 `
   -w /app -v "$(pwd):/app" `
   --network todo-app `
   -e MYSQL_HOST=mysql `
   -e MYSQL_USER=root `
   -e MYSQL_PASSWORD=secret `
   -e MYSQL_DB=todos `
   node:18-alpine `
   sh -c "yarn install && yarn run dev" 

别忘了先移除之前app的容器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们打开页面随便加点数据文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_add_items

然后我们进去mysql容器中,看下数据是否正常文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker exec -it <mysql-container-id> mysql -p todos

查看下数据文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

select * from todo_items;
docker入门不完全指南,这玩艺儿不用则废!
img_data

正常文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


使用Docker Compose[28]

现在我们的项目已经是多容器的了,但是一般一个项目一般不只有两个容器,随着容器的增多后续维护成本会增高。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

另外容器太多,要打包成镜像一个一个发布和安装就会非常繁琐。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这个时候就需要有一个管理器用来统管这些容器,比如k8s[29]文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

不过这里我们只要用的是Docker Compose文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们先来安装下(安装docker desktop的忽略,自带了):Install the Compose plugin文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

// ubuntu/debain
sudo apt-get update
sudo apt-get install docker-compose-plugin
// RPM-based distros
sudo yum update
sudo yum install docker-compose-plugin

安装完之后,可以检验下安装是否正常文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker compose version 
docker入门不完全指南,这玩艺儿不用则废!
img_dockeer_compose_version

另外里面还教了如何手动安装docker compose,如果你想要这种方式请自行翻看文档,这里就不说了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

安装完之后我们回到app文件夹中创建一个docker-compose.yml的文件文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

services:
  • services:最开始需要定义需要管理的服务或者是容器,到时候打包的时候这些定义的服务都将作为其中一部分。

然后我们定义第一部分文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

services:
  app:
    image: node:18-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 127.0.0.1:3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos
  • app: 这个是service的名字,后面会作为网络的别名,可以是任意的,不必是app
  • image:这个service的镜像
  • command:这个服务开启后要执行的指令,非必要,也不需要固定顺序,不过一般都放在image下面
  • ports:服务端口,这个项有两种写法,一种简洁,另一种详细,具体可见:short syntaxlong syntax
// short example
ports:
  - "3000"
  - "3000-3005"
  - "8000:8000"
  - "9090-9091:8080-8081"
  - "49100:22"
  - "8000-9000:80"
  - "127.0.0.1:8001:8001"
  - "127.0.0.1:5000-5010:5000-5010"
  - "6060:6060/udp"

// long example
ports:
  - target: 80
    host_ip: 127.0.0.1
    published: "8080"
    protocol: tcp
    mode: host

  - target: 80
    host_ip: 127.0.0.1
    published: "8000-9000"
    protocol: tcp
    mode: host 
  • working_dir:指令的工作目录
  • volumes:卷积的位置,相对于working_dir,这个也有长和短的两种写法,具体可见:shortlongs
  • environment:环境变量定义

ok,然后我们再来配置mysql的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

 services:
  app:
  // ...
  mysql:
    image: mysql:8.0
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos
volumes:
  todo-mysql-data:
  • volumes:注意这里的volumes是和services同级的。这个的作用是告知docker compose要创建一个todo-mysql-data的卷积,compose并不能识别是否需要自动创建卷积。详见:Volumes top-level element

最终这个docker-compose.yml内容如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

services:
  app:
    image: node:18-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 127.0.0.1:3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos
  mysql:
    image: mysql:8.0
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos
volumes:
  todo-mysql-data: 

现在我们可以来试着运行下了,不过在这之前,确保mysqlapp这俩容器都移除了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们就能运行了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker compose up -d 
docker入门不完全指南,这玩艺儿不用则废!
img_compose_up
  • -d:这个和之前介绍的是一样的,都是在后台运行

这个时候我们在docker ps -a可以看到之前移除的容器又出现了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_containers

我们再来访问下浏览器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_test_success

正常访问文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

或者你也可以看下log文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker compose logs -f <service-name>
docker入门不完全指南,这玩艺儿不用则废!
img_compose_logs

然后我们回到docker desktop中,这个时候你会发现容器只剩下一个app文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_app

另外说下移除的步骤文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker compose down

这样就移除了,不过需要注意的是默认不会移除卷积,如果你有这个需要,你需要加上参数:--volumes文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker desktop中点击右侧删除按钮即可,不过通过这种方式移除的是没法子顺便移除卷积的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_remove_compose

镜像构建最佳实践[30]

我们现在已经知道镜像是一个个layer堆叠起来的,但是我们并不清楚这些layer是哪些,这个时候就可以使用文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker image history <image-name>

这个指令来查看是哪些文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_image_history

有些内容被遮盖了,如果你想看全,你可以带上--no-trunc这个参数文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

现在我们可以看到所有的layer了,那么我们就能分析这些layer来优化以达到减少构建时间的效果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

另外提一嘴,当其中一层layer改变之后,它往上的所有层都会重新创建,因为每层layer都可能依赖于它前面那层。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

我们先来看个例子的Dockerfile文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

# syntax=docker/dockerfile:1
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"] 

这里每一个指令都会创建一层layer。而根据前面知道的知识,如果其中一层变化了,那么后面所有层都要跟着变。这个时候就很浪费时间了,因为后面的所有层我们没有改动,我们没必要每次都去让它们重新构建,比如这个yarn install --production,每次都需要重新安装依赖。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这个时候最简单的方案自然就是缓存。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这里我们只需要调整下yarn install的位置,让它在copy之前执行,那么它就只会安装依赖一次。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

# syntax=docker/dockerfile:1
 FROM node:18-alpine
 WORKDIR /app
 COPY package.json yarn.lock ./
 RUN yarn install --production
 COPY . .
 CMD ["node", "src/index.js"] 

后面除了package.json之外任何文件的改动都不会再触发这个yarn install文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们再搞个.dockerignore文件用来忽略某些会被复制到的东西,作用和.gitignore一样。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

 node_modules

详情可见:Dockerfile reference文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

更多node相关的项目可以参考这篇:Dockerizing a Node.js web app | Node.js文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

最后我们再重新构建(别忘了开魔法)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker build -t getting-started .

构建完之后我们改下src/static/index.html文件内容文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_change_title

然后我们再重新构建文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

docker入门不完全指南,这玩艺儿不用则废!
img_cached

现在构建就少了这一步了,构建耗时也是少了很多文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

然后我们来简单接触下多阶段构建(multi-stage builds)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

多阶段构建的好处:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

  • 将一些build-time依赖从runtime依赖分离,减少运行时构建时间
  • 减少镜像体积,只包含程序运行需要的。

这里以Maven/Tomcat为例子,比如JDK在编译阶段是必要的,但是对于runtime来说并不需要,所以这个时候我们就需要去掉不必要的文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

# syntax=docker/dockerfile:1
FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 

这里我们先构建maven,这个构建别名为build,然后第二阶段构建直接从maven里复制需要的内容即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

这里我不是很懂,如果说错了,麻烦评论区说下,谢谢~文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

再来个react项目的例子文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

如果不考虑SSR等,我们甚至不需要node环境,所以直接复制html/js/cssnginx上即可。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

# syntax=docker/dockerfile:1
FROM node:18 AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html 

那么到这我们的入门就差不多了文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html


总结

这玩意儿要实战才行,不然一学就会,一用就废~文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

另外docker和云原生也有关系,比如k8s文章源自菜鸟学院-https://www.cainiaoxueyuan.com/yunwei/50851.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/yunwei/50851.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定