Docker官方文档学习笔记(二):Docker Desktop入门

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Docker官方文档学习笔记(二):Docker Desktop入门

参考官方文档Get started

下载并启动教程镜像

先启动Docker Desktop,然后打开终端,输入命令:

docker run -d -p 80:80 docker/getting-started

参数说明:

  • -d 以分离模式(detached mode)运行容器(即后台运行容器)
  • -p 80:80 将主机的80端口映射到容器的80端口
  • docker/getting-started 要使用的容器的名称

第一次输入的时候,提示未运行docker daemon,启动docker desktop之后,第二次运行成功
在这里插入图片描述
多个单字符的参数可以合并,例如将-d -p 80:80简化为-dp 80:80

docker run -dp 80:80 docker/getting-started

容器运行后,通过浏览器打开http://localhost即可看到官方教程

Docker Dashboard(Docker仪表板)

使用Docker Dashboard可以方便地管理容器,如图所示(docker会给容器随机生成一个名称)
在这里插入图片描述

体验示例APP

在本教程的剩余部分,将通过运行一个Node.js的应用程序示例来演示docker如何使用

下载APP代码

可以下载app文件夹的压缩包或将整个仓库克隆下来,文件夹内有一个json和两个子文件夹
在这里插入图片描述

创建APP的容器镜像

Dockerfile是一个基于文本的用于创建容器镜像的脚本文件

  1. 在package.json所在文件夹下(package.json的同级位置)创建一个名为“Dockerfile”的文件,内容如下
# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000
  1. 在文件夹下打开终端,使用docker build命令创建容器镜像
docker build -t getting-started .

此时会发现正在下载很多“layers”,这是因为在脚本中写了

FROM node:12-alpine

来表示镜像需要从这个镜像开始构建,而电脑里没有这些镜像,所以需要下载。下载好后,通过yarn命令将app的依赖下载下来。

-t后跟参数getting-started表示将这个镜像命名为getting-started

最后的“.”表示docker build命令应该在当前目录下查找Dockerfile

不使用代理的话,下载起来可能会很慢,两种办法,换源或者使用代理

  • 若使用代理,则

    • 方法一:在运行命令的时候添加参数(不能为127.0.0.1,因为docker会使用自己创建的网卡,所以需要将代理地址改为本机ip,然后在代理软件里设置监听地址为0.0.0.0,Ubuntu软件商店的Electron不能设置监听地址,可以使用商店里的Qv2ray):
    --build-arg http_proxy=http://192.168.66.23:1080 --build-arg https_proxy=http://192.168.66.23:1080
    • 方法二:在~/.docker/config.json中添加
    "proxies": {
        "default": {
            "httpProxy": "http://192.168.66.23:1080",
            "httpsProxy": "http://192.168.66.23:1080",
            "noProxy": "localhost"
        }
    },

    如图所示

在这里插入图片描述
运行的时候不需要加参数,docker会自动配置容器的代理

- 方法三:

方法一适用于docker build命令,若docker run命令下载镜像时,在Docker Desktop的设置中设置代理即可
在这里插入图片描述

启动APP容器

  1. 使用docker run命令来启动

    docker run -dp 3000:3000 getting-started

    在这里插入图片描述
    -d -p 这两个参数在上个博客中已经介绍过了,一个是后台运行,一个是指定端口映射。如果没有端口映射,就无法访问app。

  2. 运行后,在浏览器中输入地址http://localhost:3000就可以看到app,界面如图所示

在这里插入图片描述

  1. 在New Item栏输入内容,然后点击Add Item添加,可以看到已经加入了内容,点击左侧的方框可以标记内容,点击右侧的红色垃圾桶可以删除内容

在这里插入图片描述

  1. 此时打开Docker DashBoard,可以看到正在运行的app容器

在这里插入图片描述
接下来学习如何更新app并且用新的镜像来更新我们正在运行的app

更新APP

以将页面中的文字由

No items yet! Add one above!

更换为
You have no todo items yet! Add one above!

为例

更新源代码

  1. 在src/static/js/app.js文件中,将第56行的
<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>

在这里插入图片描述

  1. 构建容器
docker build -t getting-started .

因为我使用了代理,所以添加了代理参数后的命令为

docker build -t getting-started . --build-arg http_proxy=http://192.168.66.23:1080 --build-arg https_proxy=http://192.168.66.23:1080
  1. 运行容器
docker run -dp 3000:3000 getting-started

会报错
在这里插入图片描述
这是因为旧容器还在运行,占用了3000端口,要解决这个问题,需要删除旧容器

删除旧容器

要删除一个容器,首先需要停止运行这个容器。当它停止运行后,有两种方法删除旧容器,任选一个即可。

  • 方法一:使用命令删除容器

    1. 使用docker ps命令获取容器的ID

      docker ps
    2. 使用docker stop命令停止容器

      docker stop 容器ID
    3. 使用docker rm命令删除容器

      docker rm 容器ID

    可以将步骤2和3简化为一个步骤,即使用docker rm -f命令删除容器

        docker rm -f 容器ID
  • 方法二:使用Docker Dashboard删除容器

    1. 打开Dashboard,找到要删除的容器

    在这里插入图片描述

    1. 点击右侧的删除

    在这里插入图片描述

点击Delete Forever确认删除即可

![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/929fe4fda68748898978550e6daace51.png#pic_center)

启动刚才更新了APP的容器

  1. 执行命令

    docker run -dp 3000:3000 getting-started
  2. 刷新浏览器页面或重新打开http://localhost:3000/即可看到更新成功
    在这里插入图片描述

小结

以上的更新APP的方法存在两个问题:

  1. 更新完后所有的Item都丢失了,即原先的数据都丢失了。
  2. 为了仅仅实现将这么小的一个改动部署,却执行了很多步。

共享APP

Docker Hub 是全世界最大的Docker容器镜像仓库和社区,如果没有账号的话,需要 注册Docker ID 才能登录

初始化用户

docker desktop使用 pass 存储gpg密钥,从Docker Dashboard或Docker menu登录Docker Hub前,必须初始化pass。生成gpg密钥后,就可以实现 docker login 的时候不输入密码,使用 Docker Hub生成的访问令牌 登录

  1. 生成gpg密钥

    gpg --generate-key

    生成后将输出内容中的pub部分复制

  2. 初始化pass

    pass init 刚才复制的pub部分
  3. 登录Docker Desktop或使用docker login命令登录即可,使用docker hub生成的访问令牌登录时,注意保管好令牌,因为生成之后只在生成完成的时候显示一次,登录的时候将输入的密码改为输入生成的令牌即可

若要删除密钥,需要删除公钥和私钥,推荐使用系统应用程序菜单中自带的密码管理器

# 删除私钥
gpg --delete-secret-keys 私钥

# 删除公钥
gpg --delete-keys 公钥

若要删除pass中的密码信息

pass rm gpg生成密钥时使用的邮箱

创建一个仓库

为了能够推送(push)镜像,首先需要在Docker Hub上创建一个仓库

  1. 登录Docker Hub
  2. 点击创建仓库按钮“Create a Repository”

在这里插入图片描述
使用“getting-started”作为仓库名称,并且将可访问性设置为公开,然后点击创建按钮“Create”

推送镜像

  1. 打开命令行,输入仓库显示的推送命令

    docker push 用户名/getting-started

    例如用户名为docker,则推送命令为

    docker push docker/getting-started

    上面的推送命令会执行失败,这是因为这条命令会查找名为 docker/getting-started 的镜像,但是并没有找到。如果运行 docker image ls 命令,会发现列出的镜像里也没有这个镜像。(如果之前在 下载并启动教程镜像 这一部分中下载了教程镜像,则这个命令会开始执行,并且使用 docker image ls 命令会列出该镜像)

  2. 使用命令登录 Docker Hub

    docker login -u 用户名
  3. 使用 docker tag 命令给getting-started镜像命名一个新名称

    docker tag getting-started 用户名/getting-started
  4. 现在尝试重新推送镜像,如果是从 Docker Hub 复制的命令:

    docker push 用户名/getting-started:tagname

    那么现在不需要输入tagname部分,只需使用 1. 中的命令即可,因为并没有添加tagname。如果推送的时候不指定tag,Docker将使用名为latest的tag

在新的实例上运行镜像

  1. 打开 Play with Docker 并登录
  2. 点击 ADD NEW INSTANCE 添加实例

在这里插入图片描述

  1. 在右侧的终端中输入命令

    docker run -dp 3000:3000 你的用户名/getting-started
  2. 点击右上角的open port输入3000,或点击3000按钮,即可看到更新的界面

小结

在这一部分中,我们学习了如何将镜像推送到仓库来分享我们的镜像,并在全新的实例中运行推送的最新镜像。接下来我们将学习如何在重启容器后还能保持数据。

保留容器的数据(库)

每次我们启动容器的时候,todo list中的数据就会被清除,为什么会是这种情况?让我们研究一下容器是如何工作的。

容器的文件系统

当一个容器运行的时候,它会使用一个镜像中的很多layers来作为它的文件系统。每个容器也有自己的“暂存空间”(scratch space)用于创建、升级、删除文件。任何改变都不会在别的容器中看到,及时它们使用的是相同的镜像。

  • 通过练习查看容器的文件系统
    我们将启动两个容器并各自创建一个文件。你将看到一个容器中创建的文件在另一个容器中无法使用。
  1. 启动 ubuntu 镜像并把1~10000之间的一个随机数写入一个名为data.txt的文件

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"

    我们通过 && 以使用两个命令,第一个命令为生成随机数并写入data.txt中,第二个命令只是监视文件以保持容器运行(The second command is simply watching a file to keep the container running.)

  2. 点击 open in terminal
    在这里插入图片描述

在打开的终端中输入以下命令以查看文件内容

cat /data.txt

在这里插入图片描述
如果想在自己的终端中运行命令,则使用以下命令(容器ID需要使用 docker ps 命令获取)

docker exec 容器ID cat /data.txt
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/1637ca285d754d2fbfed77e311631579.png#pic_center)

同样可以看到生成的随机数

  1. 使用以下命令删除刚才的容器

    docker rm -f 容器ID

    在这里插入图片描述

容器卷(Container volumes)

在先前的实验中,我们看到每个容器每次启动时都从镜像定义开始(we saw that each container starts from the image definition each time it starts)。尽管容器可以创建、更新和删除文件,这些变动都会在容器删除时丢失并且容器间所有的改变都是独立的。使用卷,我们可以改变这些情况。
卷为容器提供了连接主机的特定路径下的文件系统的能力(Volumes provide the ability to connect specific filesystem paths of the container back to the host machine.)。如果容器中的一个目录被挂载,这个目录中的变动就可以被主机看到。如果我们跨容器重新启动装载相同的目录,我们将看到相同的文件(If we mount that same directory across container restarts, we’d see the same files.)。
卷主要有两种类型。这两种类型我们最后都会使用,但是开始的时候会用命名卷(named volumes)

使用命名卷(named volume)保留todo数据

todo app默认将它的数据使用 SQLite Database 存储在容器的文件系统内的 /etc/todos/todo.db 下。这是一种简单的关系型数据库,所有的数据都存在一个文件中,稍后我们将讨论如何将其切换到其他数据库引擎。

  1. 使用 docker volume create 命令创建一个卷

    docker volume create todo-db
  2. 在Docker Dashboard中停止并删除todo app容器(或使用 docker rm -f 命令)
  3. 启动todo app容器,但在启动时加入 -v 参数来指定要挂载的卷。我们将使用命名卷并将它挂载到 /etc/todos 。

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
  4. 容器启动后,打开app并在todo list中添加一些项目

在这里插入图片描述

  1. 重复步骤 2. ,删除容器
  2. 重复步骤 3. ,启动一个新的容器
  3. 打开app,你会发现你在上个app容器添加的项目还在

进一步了解卷

使用 docker volume inspect 命令查看卷

docker volume inspect todo-db

在这里插入图片描述
Mountpoint 是数据实际存储的位置

小结

现在,我们有了一个可以在重启之后继续运行的应用程序!然而在之前的步骤中我们看到,对于每个变动,都需要花费相当一部分时间重新构建镜像以使得变动生效。有一个更好的办法来使这些变动生效,那就是绑定挂载。

使用绑定挂载(bind mounts)

在之前的章节中,我们讲解了使用命名卷named volume)来保留我们数据库中的数据。如果我们只是简单地存储数据,命名卷会非常好用,因为我们不必担心数据存放的具体位置。
使用绑定挂载,我们可以在主机上指定挂载点,我们可以用这个方法来存储数据,但是它经常用于向容器提供额外的数据。当容器工作时,我们可以使用绑定挂载来向容器挂载我们的源码以使得容器能看到代码的更改、响应并让我们立即看到更改。

快速比较不同类型的卷之间的区别

  命名卷(Named Volumes) 绑定挂载(Bind Mounts)
数据在主机中存放的位置 由Docker决定 由用户指定
挂载示例 (使用 -v) my-volume:/usr/local/data /path/to/data:/usr/local/data
用容器内容填充新卷(Populates new volume with container contents) 支持 不支持
支持卷驱动程序(Supports Volume Drivers) 支持 不支持

启动一个开发模式的容器

为了使我们的容器支持开发流程,我们将执行以下步骤

  • 将我们的源码挂载到容器
  • 安装所有的依赖,包括“dev”依赖
  • 启动 nodemon 以监视文件更改
  1. 确保你现在没有任何正在运行的 getting-started 容器
  2. 在app所在目录下运行以下命令,在之后我们会解释这些命令
    对于Linux,运行

    docker run -dp 3000:3000 \
     -w /app -v "$(pwd):/app" \
     node:12-alpine \
     sh -c "yarn install && yarn run dev"

    如果是在Windows,在PowerShell中运行以下命令

    docker run -dp 3000:3000 `
    -w /app -v "$(pwd):/app" `
    node:12-alpine `
    sh -c "yarn install && yarn run dev"

    如果是在 Apple silicon Mac 或其他 ARM64 设备,运行以下命令

    docker run -dp 3000:3000 \
     -w /app -v "$(pwd):/app" \
     node:12-alpine \
     sh -c "apk add --no-cache python2 g++ make && yarn install && yarn run dev"

    命令解释:
    -dp 3000:3000

    和之前一样,分离(后台)模式运行并创建一个端口的映射

    -w /app

    设置运行命令的“工作目录”或当前目录

    -v "$(pwd):/app"

    将当前目录挂载到容器的 /app 目录

    node:12-alpine

    要使用的镜像

    sh -c "yarn install && yarn run dev"

    在容器中要执行的命令。使用sh(alpine 没有bash)运行 yarn install 安装所有依赖,然后运行 yarn run dev。如果看一下 package.json 就会看到 dev 脚本是在启动 nodemon。
  3. 使用 docker logs 命令查看输出日志。当你看到这种输出时就可以知道你可以继续接下来的步骤了

    docker logs -f 容器ID

    如果要关闭查看,按下 Ctrl+C 即可退出

    既可以使用docker ps命令查看正在运行的容器的信息,也可以使用 docker container ls 列出所有容器的信息(docker image ls 是列出所有镜像)

  4. 现在对app进行修改。在 src/static/js/app.js 文件中,将109行的 “Add Item”改为“Add”
  5. 只需要刷新app页面(或重新打开)就可以立即看到改动。可能需要等待node服务器重启,所以如果页面报错,等待一会然后再刷新,当使用步骤 3. 看到如下输出时,说明成功启动

在这里插入图片描述
在后面的教程中我们会介绍 Docker Compose ,这会简化我们的命令(我们已经使用了很多参数了,例如-dp -v -t等)

小结

通过使用绑定挂载,我们可以快速响应需求并发布更改。在接下来的教程中,将会介绍如何在容器中使用MySQL来代替SQLite,并使多个容器互相通信。

多容器的APP

目前为止,我们已经能够使用单个容器运行app。但是现在需要向app添加MySQL,那么就会出现接下来的问题——“MySQL应该在哪运行?是安装在app所在的容器中并运行吗?”
总的来说,每个容器应该只能做一件事并做好,理由如下:

  • 很有可能您必须以不同于数据库的方式扩展API和前端(There’s a good chance you’d have to scale APIs and front-ends differently than databases)
  • 单独的容器允许您独立地进行版本和更新(Separate containers let you version and update versions in isolation)
  • 虽然您可以在本地为数据库使用容器,但您可能希望在生产中为数据库使用托管服务。那么,你不想在应用程序中附带数据库引擎。(While you may use a container for the database locally, you may want to use a managed service for the database in production. You don’t want to ship your database engine with your app then.)
  • 运行多个进程将需要一个进程管理器(容器只启动一个进程),这会增加容器启动/关闭的复杂性(Running multiple processes will require a process manager (the container only starts one process), which adds complexity to container startup/shutdown)

app的架构将升级如下
在这里插入图片描述

容器网络

容器默认情况下是独立运行的,不知道运行在同一机器上的其他进程或容器的任何信息。在同一网络下的两个容器可以互相通信。

启动MySQL

有两种让容器联网的方法:

  • 启动时分配
  • 连接现有容器

现在我们将创建一个网络并在MySQL容器启动时连接

  1. 创建网络

    docker network create todo-app
  2. 启动MySQL容器并连接创建的网络。同时还会设置一些环境变量以供数据库初始化自己时使用(详情见官方文档的“Environment Variables”)

Linux系统使用以下命令

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:5.7
 如果是基于ARM的芯片,比如M1,使用以下命令
 ```bash
 docker run -d \
 --network todo-app --network-alias mysql \
 --platform "linux/amd64" \
 -v todo-mysql-data:/var/lib/mysql \
 -e MYSQL_ROOT_PASSWORD=secret \
 -e MYSQL_DATABASE=todos \
 mysql:5.7
 ```
 如果是Windows,在PowerShell中使用以下命令
 ```bash
 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:5.7
 ```
 我们指定了--network-alias参数,一会我们会回过头来讨论这个。

 我们正在使用挂载在存放MySQL数据的 /var/lib/mysql 下的名为 todo-mysql-data 的卷。然而我们从未使用 docker volume create 命令。Docker会识别我们想要的命名卷的名称并自动为我们创建这个卷。
 
  1. 为了确认我们已经启动了数据库,使用以下命令连接数据库

    docker exec -it MySQL容器的ID mysql -u root -p

    输入密码时,输入secret

    在MySQL Shell中,列出数据库并确认你看到了 todos 数据库

    SHOW DATABASES;

    你应该看到类似这种的输出

在这里插入图片描述

连接MySQL

使用 nicolaka/netshoot 容器(附带有许多工具,对于解决或调试网络问题非常有用)

  1. 新建一个使用 nicolaka/netshoot 的容器,确保它连接到和MySQL容器相同的网络

    docker run -it --network todo-app nicolaka/netshoot
  2. 在容器中,使用 dig 命令,这是一个很有用的DNS工具

    dig mysql

    会看到类似如下的输出
    在这里插入图片描述

在“ANSWER SECTION”中,你会看到MySQL有一个记录 A 解析172.18.0.2。虽然mysql通常不是有效的主机名,但Docker能够将其解析为具有该网络别名的容器的IP地址(因为之前使用了--network alias参数)。这意味着我们的app只需要连接到名为mysql的主机就可以和数据库通信了。

使用MySQL运行app

todo app支持使用以下环境变量来设置MySQL的连接:

  • MYSQL_HOST
运行MySQL服务器的主机名
  • MYSQL_USER
连接MySQL使用的用户名
  • MYSQL_PASSWORD
连接MySQL使用的密码
  • MYSQL_DB
要使用的数据库的名称
  1. 注意:对于8.0及以上版本的MySQL,确保在mysql中运行以下命令(Note: for MySQL versions 8.0 and higher, make sure to include the following commands in mysql)

    ALTER USER 'root' IDENTIFIED WITH mysql_native_password BY 'secret';
     flush privileges;
  2. 设置上面的每一个环境变量,并将容器连接到app网络(同样在app所在目录下执行)
    Linux系统运行以下命令

    docker run -dp 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:12-alpine \
    sh -c "yarn install && yarn run dev"

    如果使用的Windows,在PowerShell中运行以下命令

    docker run -dp 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:12-alpine `
    sh -c "yarn install && yarn run dev"
  3. 使用 docker logs 容器ID 命令会看到类似如下的输出

在这里插入图片描述

  1. 打开app,添加一些item
  2. 连接数据库,密码secret

    docker exec -it MySQL容器ID mysql -p todos

    然后在mysql shell中输入以下命令

    select * from todo_items;

    可以看到以下输出
    在这里插入图片描述

打开Docker DashBoard,可以看到这两个容器都在运行,但没有迹象表明它们在一个应用程序中组合在一起,接下来就会介绍如何使用 Docker Compose 改善这种情况。

小结

在这部分教程中,我们实现了一个将数据存储在另一个容器中的应用程序。我们学习了一点关于容器网络的内容并看到了如何使用DNS执行服务发现。
在下一个教程中,我们将介绍 Docker Compose。通过 Docker Compose 可以以一种非常容易的方式将我们的应用程序分享出去,并且其他人只需要一个命令就能启动它们。

使用Docker Compose

Docker Compose 是一个用于帮助定义并分享多容器应用程序的工具。使用Compose,我们可以创建一个YAML文件来定义服务,并且只需一个命令就能启用或关闭它们的每一个。
使用Compose最大的优点就是你可以在一个文件中定义你的应用程序栈,将这个文件放在你的项目仓库的根目录。其他用户只需要clone你的仓库并启动compose程序就可以使用。

安装Docker Compose

按照官方文档的说法,如果你在Windows或Mac上安装了 Docker Desktop 或 Docker Toolbox ,你就已经有了 Docker Compose 。 Play-with-Docker 实例同样已经安装了 Docker Compose 。如果你使用的是Linux设备,你需要安装 Docker Compose 实际上,Linux上如果安装了Docker Desktop也不需要再手动安装 Docker Compose。
文档中给的查看是否安装的命令是

docker-compose version

但经过实际测试,命令为

docker compose version

在这里插入图片描述

创建Compose文件

  1. 在app项目的根目录,创建一个名为 docker-compose.yml的文件
  2. 在Compose文件中,我们将从定义模式版本开始。在大部分情况下,最好使用最新版本。关于不同版本和兼容性的详细信息参见官方文档 compose-versioning

    version: "3.7"
  3. 下一步,我们将定义作为应用程序的一部分运行的服务(或容器)列表

    version: "3.7"
    
    services:

定义应用程序服务

这是我们之前定义容器的时候所用的命令
Linux使用

 docker run -dp 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:12-alpine \
  sh -c "yarn install && yarn run dev"

Windows在PowerShell中使用

docker run -dp 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:12-alpine `
  sh -c "yarn install && yarn run dev"
  1. 首先,定义服务入口和容器的镜像。我们可以为服务选择任何名称,这个名称将自动成为网络的别名,这在定义我们的MySQL服务时会很有用。
version: "3.7"

services:
  app:
  image: node:12-alpine
  1. 使用 command 定义要执行的命令
version: "3.7"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
  1. 使用 ports 定义端口映射
version: "3.7"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
  1. 使用 working_dir 定义工作目录,使用 volumes 定义卷。卷的定义的写法有两种,长写法短写法
version: "3.7"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
  1. 使用 environment 定义环境变量
version: "3.7"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

定义MySQL服务

在之前的教程中使用以下命令运行容器
Linux下使用

 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:5.7

Windows下在PowerShell中使用

 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:5.7
  1. 定义名为 mysql 的服务,这样它就会自动获得网络别名。然后定义镜像。
version: "3.7"

services:
  app:
    # The app service definition
  mysql:
    image: mysql:5.7
  1. 接下来使用 volumes 定义卷映射,如果只是简单的提供卷的名称,则compose使用的是默认选项。定义更多内容参见Volume configuration reference
version: "3.7"

services:
  app:
    # The app service definition
  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql

volumes:
  todo-mysql-data:
  1. 最后指定环境变量
version: "3.7"

services:
  app:
    # The app service definition
  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:

现在,docker-compose.yml应该看起来是这样的

version: "3.7"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:

运行应用程序栈

  1. 确保没有正在运行的其他容器
  2. 使用 docker compose up 命令运行应用程序,-d 参数意为后台运行所有内容

    docker compose up -d

在这里插入图片描述
Docker Compose 会自动为应用程序栈创建相应名称的网络(这就是为什么我们没有在compose文件中定义一个网络)

  1. 现在通过 docker compose logs -f 命令查看日志,你将在一个流里面看到每个服务实时输出的日志,因为 -f 参数意为“follow”,所以会提供实时输出。

    docker compose logs -f

    在这里插入图片描述

如果要查看特定服务的输出,在 -f 后加上服务的名称即可
在这里插入图片描述

在 Docker DashBoard 中查看应用程序栈

在这里插入图片描述
可以看到有一个名为app的分组。默认情况下,docker-compose.yml所在的文件夹的名称(即项目名称)会作为分组名称。

在这里插入图片描述

点击进入分组,里面所含的容器的名称也很有描述性,遵循

<project-name>_<service-name>_<replica-number>

即:项目名称_服务名称_副本编号 的命名方式。

删除容器组(包含其中的容器)和网络

使用 docker compose down 命令或点击 Docker Dashboard 中分组栏的垃圾桶图标来删除,默认情况下两种方式都不会删除创建的卷,如果需要删除相应的卷,在命令后加上 --volumes 参数或点击 Docker Dashboard 的 Volumes 栏找到卷并删除
在这里插入图片描述

镜像构建最佳实践

详细内容参见官方文档

安全扫描

构建镜像后,最好使用 docker scan命令扫描它以查找安全漏洞。Docker 与 Snyk 合作提供漏洞扫描服务。需要使用 docker scan --login 登录后才能使用扫描。
扫描结果的输出会列出漏洞的类型、了解更多信息的 URL,以及重要的是相关库的哪个版本修复了漏洞。

查看镜像层(layer)

使用 docker image history 可以查看用于在镜像中创建每个镜像层的命令,添加 --no-trunc 参数可以获得完整输出,例如

docker image history --no-trunc getting-started

镜像层缓存

使用缓存可以减少容器构建时间。因为一旦某个层发生变化,所有的下层也会被重新构建。
回顾之前为getting-started创建的Dockerfile

# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000

再回想 docker image history 的输出,可以看到Dockerfile中的每个命令最后都成了镜像的一层。你可能还记得每次我们向镜像中做出变动,yarn的依赖就会被重新安装一次。
为了解决这个问题,我们需要重构我们的Dockerfile以便支持缓存依赖。对于基于node应用程序,这些依赖都位于package.json中。

  1. 首先将package.json复制进Dockerfile,然后再复制剩下的所有内容
# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --production
COPY . .
CMD ["node", "src/index.js"]
EXPOSE 3000
  1. 在Dockerfile所在目录下创建一个 .dockerignore 文件,写入以下内容
node_modules
  1. 使用 docker build 命令构建镜像
docker build -t getting-started .
  1. 现在改变 src/static/index.html 中的内容(比如将 \<title> 中的内容改为“The Awesome Todo App”)
  2. 重复步骤 3. ,你将看到类似这种的输出

在这里插入图片描述

多阶段构建

Maven/Tomcat 示例

在构建基于 Java 的应用程序时,需要一个 JDK 来将源代码编译为 Java 字节码。但是,生产中不需要 JDK。此外,您可能正在使用 Maven 或 Gradle 等工具来帮助构建应用程序。在我们的最终镜像中也不需要这些。多阶段构建可以帮助解决这个问题。

# 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 

在此示例中,我们使用一个阶段(称为build)来使用 Maven 执行实际的 Java 构建。在第二阶段(从 FROM tomcat 开始),我们从 build 阶段复制文件。最终镜像只在最后阶段构建(可以使用 --target 标志覆盖)。

React 示例

在构建 React 应用程序时,我们需要一个 Node 环境来将 JS 代码(通常是 JSX)、SASS 样式表等编译成静态 HTML、JS 和 CSS。如果我们不进行服务器端渲染,我们甚至不需要 Node 环境来进行生产构建。为什么不在静态 nginx 容器中发送静态资源?

# syntax=docker/dockerfile:1
FROM node:12 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

在这里,我们用了 node:12 镜像来构建(最大化层缓存),然后将输出复制到Nginx容器中

回顾

通过简单了解镜像的结构,我们可以更快地构建镜像并减少更改。扫描镜像让我们确保我们正在运行和分享的容器是安全的。多阶段构建还能通过将构建时依赖项与运行时依赖项分开,帮助我们减少整体镜像大小并提高最终容器的安全性。

相关实践学习
通过容器镜像仓库与容器服务快速部署spring-hello应用
本教程主要讲述如何将本地Java代码程序上传并在云端以容器化的构建、传输和运行。
Kubernetes极速入门
Kubernetes(K8S)是Google在2014年发布的一个开源项目,用于自动化容器化应用程序的部署、扩展和管理。Kubernetes通常结合docker容器工作,并且整合多个运行着docker容器的主机集群。 本课程从Kubernetes的简介、功能、架构,集群的概念、工具及部署等各个方面进行了详细的讲解及展示,通过对本课程的学习,可以对Kubernetes有一个较为全面的认识,并初步掌握Kubernetes相关的安装部署及使用技巧。本课程由黑马程序员提供。 &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
7天前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
28 2
|
1月前
|
Kubernetes Cloud Native 云计算
云原生入门:从Docker到Kubernetes的旅程
【10月更文挑战第2天】本文将带你走进云原生的世界,从基础的Docker容器技术开始,逐步深入到Kubernetes集群管理。我们将通过实际代码示例,探索如何利用这些工具构建、部署和管理现代云应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的知识和技能,让你在云原生领域迈出坚实的一步。
85 5
|
9天前
|
Cloud Native 持续交付 Docker
Docker容器化技术:从入门到实践
Docker容器化技术:从入门到实践
|
15天前
|
运维 持续交付 虚拟化
docker入门详解!!!
本文介绍了容器技术的发展历程,从物理机到虚拟化再到容器化,重点讲解了Docker的诞生及其优势。Docker通过轻量级的容器技术,实现了资源的高效利用、快速启动、环境一致性、持续交付和部署等优点。文章还详细解析了Docker的架构和工作原理,包括Docker Daemon、REST接口、Docker Client等组件,以及容器与虚拟机的差异。
54 2
|
22天前
|
Kubernetes Cloud Native 开发者
云原生技术入门:Kubernetes和Docker的协作之旅
【10月更文挑战第22天】在数字化转型的浪潮中,云原生技术成为推动企业创新的重要力量。本文旨在通过浅显易懂的语言,引领读者步入云原生的世界,着重介绍Kubernetes和Docker如何携手打造弹性、可扩展的云环境。我们将从基础概念入手,逐步深入到它们在实际场景中的应用,以及如何简化部署和管理过程。文章不仅为初学者提供入门指南,还为有一定基础的开发者提供实践参考,共同探索云原生技术的无限可能。
33 3
|
27天前
|
Ubuntu Shell 开发者
Docker入门:轻松开始容器化之旅
【10月更文挑战第17天】Docker 是一种开源的应用容器引擎,它让开发者能够“一次构建、到处运行”。Docker 通过容器化技术将应用程序及其依赖打包在一起,从而确保应用在任何环境中都能一致地运行。本文将为新手用户提供一个全面的Docker入门指南,包括基本概念、优势、安装配置以及如何创建和管理容器。
45 2
|
1月前
|
存储 运维 云计算
探索Docker容器化:从入门到实践
在这个快速发展的云计算时代,Docker容器化技术正在改变应用的开发、部署和管理方式。本文旨在为初学者提供一个关于Docker的全面入门指南,并通过实践案例展示Docker在实际开发中的应用。我们将一起了解Docker的核心概念、基本操作、网络和存储,以及如何构建和部署一个简单的Web应用。无论你是开发者还是运维人员,本文都会帮助你快速掌握Docker的核心技能。
|
1月前
|
缓存 运维 Docker
容器化运维:Docker Desktop 占用磁盘空间过大?教你轻松解决!
Windows Docker Desktop 使用过程中,因镜像、容器数据及构建缓存的累积,可能导致磁盘空间占用过高。通过删除无用镜像与容器、压缩磁盘以及清理构建缓存等方法,可有效释放空间。具体步骤包括关闭WSL、使用`diskpart`工具压缩虚拟磁盘、执行`docker buildx prune -f`清理缓存等。这些操作能显著减少磁盘占用,提升系统性能。
315 4
|
1月前
|
数据可视化 数据挖掘 Docker
Docker Desktop 安装 ClickHouse 超级简单教程
Docker Desktop 安装 ClickHouse 超级简单教程
46 1
|
1月前
|
安全 网络安全 开发者
Docker学习笔记(一):Docker命令总结
本文介绍了Docker的基本概念、优点以及常用命令。Docker是一个开源的应用容器引擎,它通过容器打包应用程序及其依赖项,实现快速部署和迁移。主要优点包括轻量级、可移植性、易于管理、安全性和开源性。文章还区分了镜像和容器的概念,并提供了构建镜像、查看容器、运行容器、停止和删除容器等常用Docker命令的示例。
129 0