Fed dead && Rd won:从Docker到前端部署

991 阅读12分钟

前言

我不懂docker,我只是前端的搬运工。

工作中或者看一些优质项目的时候, 经常会看到Dockerfiledocker-build.sh,遗憾的是不知哪一条命令会调用此文件,庆幸的是他并不会影响我npm run。抱着try一try的态度去学习——应用——总结

ChatGpt告诉我

快速入门

文档入手。

邂逅docker

架构图

官方解释:docker客户端与docker守护程序(docker deamon)进行通讯,该守护进程运行和分发docker容器的繁重工作,docker客户端和守护程序可以在同一系统上运行,也可以将docker客户端连接到远程docker守护程序。Docker客户端和守护程序在UNIX套接字或网络接口上使用REST API进行通信。

当我看到这张架构图的时候,联想到了NodeJS: jsCode => V8 => {(eventloop) => LIBUV}

  • Docker守护程序(docker daemon => dockerd):处理api相关的请求,监听网络端口;
  • Docker客户端(docker client):指“用户”,当docker run,会将其发送到dockerd,该docker命令使用Docker API;
  • Docker注册表(docker Registry):主要存储docker镜像,并且默认情况下,在Docker Hub上查找镜像;
  • Docker对象:包括 Images, Containers
    • Images: 只读模板,用来创建容器,可以使用Dockerfile,快速创建,
    • Containers:运行image的实例,可以使用Docker API或CLI创建,启动,停止,移动或删除容器
    • 也就是说通过Dockerfile构建出镜像,然后通过镜像来创建容器,程序运行在容器中

安装(windows)

为了方便打包前端项目, 以及后续VSCode中使用docker插件,这里仅介绍windows版本

官网下载安装包,安装过程中一切都顺利,点击“图标”运行程序可出现“异常”提示信息:

Update the WSL kernel by running "wsl --update" or follow instructions at docs.microsoft.com/windows/wsl….

需要启用 WSL 2

查阅文档发现,官方给出的这部分解决方案似乎有些模糊——略过详细文档

首先启动Hyper-V Windows功能 ,具体见微软官网

注意:Hyper-V 作为可选功能内置于 Windows -- 无需下载 Hyper-V。

其次下载并安装Linux内核更新程序包,具体见微软官网,步骤一到四一次执行

点击打开软件,正常启动~

Demo Start

通过 small demo 来初步了解docker的操作流程和配置文件

克隆一个demo:git clone https://github.com/Thunder7991/getting-started.git

接下来创建容器镜像

切换目录到: cd app

创建Dockerfile:type nul > Dockerfile

写入dockerfile文件:

# syntax=docker/dockerfile:1  (1)
   
FROM node:18-alpine  (2)
WORKDIR /app (3)
COPY . . (4)
RUN yarn install --production (5)
CMD ["node", "src/index.js"] (6)
EXPOSE 3000 (7)
  • (1)指示Dockerfile中的特殊语法,用于指定Docker引擎使用的Dockerfile语法版本。在这个例子中,使用的是Dockerfile语法版本1,它是Dockerfile语法中最早的版本,支持的功能比较有限。目前,Dockerfile语法版本已经更新到3.0。

    常见的可能还有很多吧,场景中遇到类似语法的欢迎JYM补充~

  • (2)指定基础镜像(node:18-alpine),可以是官方仓库中的镜像,也可以是第三方仓库中的镜像,或者是自己构建的镜像。

  • (3)复制文件或目录到镜像中的指定路径,即在Docker容器中运行命令时的默认目录,如果使用相对路径,那么相对路径将相对于上一个WORKDIR指令设置的目录,或者如果没有设置过,则相对于/根目录。也就是说容器所在镜像中的位置为/app

  • (4)复制文件或目录到镜像中的指定路径:语法COPY <src> <dest>,其中<src>是要复制的本地文件或目录的路径,可以使用相对或绝对路径;<dest>是容器中要复制到的目标路径,必须是绝对路径。 上面例子是将/app中所有的文件复制到/app ,参考WORKDIR的位置, 在看个例子

    • WORKDIR /usr/src/app COPY ./app .:将本地./app目录中的所有文件复制到容器中的/usr/src/app目录

COPY指令还支持一些可选参数,用于指定复制操作的选项:

--chown=<user>:<group>:设置复制后的文件或目录的所属用户和组,例如--chown=root:root

--from=<image>:从另一个镜像中复制文件或目录,例如--from=ubuntu:latest

--no-cache:禁用缓存,每次构建都会重新复制文件或目录。

--follow-symlinks:复制符号链接的目标文件而不是符号链接本身。

  • (5)在镜像中运行命令或脚本
  • (6)指定容器启动时要运行的命令或脚本
    • cmd有两种传参格式:
      • CMD ["executable", "param1", "param2"]:使用JSON数组格式,指定要执行的可执行文件和参数, 例如上面代码:CMD ["node", "app.js"] => node app.js
      • CMD command param1 param2:使用命令行格式,指定要执行的命令和参数,例如:CMD npm start=> npm start

CMD指令的参数会传递给Docker守护进程,并被解释为容器的默认命令或程序。如果在运行容器时指定了其他命令或程序,那么它们会覆盖CMD指令设置的默认值。 在上面的Dockerfile中,如果需要覆盖npm start,可以在运行容器时指定一个新的命令如:docker run -it myapp bash , bash命令会覆盖CMD指令设置的默认值。

Knowledge就好像一个tree,-it如下图:

-it

  • (7) 告诉Docker守护进程容器应该监听哪些网络端口,举例:

应用程序在容器内部监听8080端口

EXPOSE 8080

在运行容器的时候,通过-p选项将该端口映射到主机上的某个端口

docker run -p 8080:8080 myimage

这将会将容器内的8080端口映射到主机的8080端口,从而使得你可以通过访问http://localhost:8080来访问应用程序。

需要注意的是,EXPOSE并不会自动将端口暴露到主机上,你还需要使用-p选项来手动指定映射端口。同时,EXPOSE也不会自动打开容器内部的端口,你需要在Dockerfile或容器启动脚本中显式地打开它们。

接下来, 我们使用命令构建容器镜像

我们切换到Clone下的项目目录,切换到app下

运行命令:

docker build -t thunderchen .

这里的-t的作用就是为构建的镜像打标签(tag)从而方便后续的管理和使用 , 最后的参数.表示当前目录,如果没有 -t *,默认标签为latest

理解万岁,这不是公理也不是定理,这只不过是软件程序如此设计的罢了,编程不就是这样吗?

此时build命令使用DockerFile来构建镜像,下图中解析到node:18-alpine,并没有该镜像,因此需要下载,依次执行文件中的命令,现在就有thunderchen 镜像了。

docker build

紧接着启动容器:

docker run -dp 3000:3000 thunderchen  
//或者给容器添加名称
docker run -dp 3000:3000 --name clearlove  thunderchen

这里的-d作用是以后台运的方式启动容器。也就是说,容器会在后台运行,并且不会在当前终端中输出日志信息。

-p上文已经说明

此时我们打开Docker Desktop软件

images

containers

访问:http://localhost:3000/

当然也可以通过docker ps 查看正在运行的Docker容器:

image.png

添加参数-a 可以列出所有容器,包括正在运行的和已停止的

好了,Base片段到此结束。

Demo Update

我们尝试更改项目中的源代码 src/static/js/app.js:

 <p className="text-center">我是修修改后的代码</p>

再次build,docker build -t thunderchen .

再次run,docker run -dp 3000:3000 thunderchen

会出现报错:

ocker: Error response from daemon: driver failed programming external connectivity on endpoint eager_cannon (9d72ad7b43f1737b0bb9ffaa425ff5620c8f380433d08f78be068176a16a87e8): Bind for 0.0.0.0:3000 failed: port is already allocated.

原因很简单,无法在旧容器仍在运行时启动新容器。 原因是旧容器已经在使用主机的端口3000,并且在计算机上只有一个进程(包括容器)可以收听特定的端口。因此我们需要卸载旧的容器

删除旧容器:

首先Docker Desktop中,操作非常简单,首先删除容器Containers,再次删除对应的Images即可。

命令行删除分三步走:

  1. 首先获取容器的Id, docker ps ps

  2. 根据Id停止容器运行:docker stop stop

  3. 根据Id删除容器:docker rm

    rm 此时你去Docker Desktop查看Containers是没有任何容器的

当然也可以将停止和删除容器一起操作:docker rm -f <the-container-id>

再次运行docker run -dp 3000:3000 thunderchen即可

补充,如何命令行删除镜像?

删除镜像前,必须保证镜像内部的容器全部删除,使用docker rmi [OPTIONS] IMAGE [IMAGE...]OPTIONS 是可选的选项,可以用来指定删除镜像时的一些参数;IMAGE 是要删除的镜像名称或镜像 ID。 删除thunderchen镜像:docker rmi thunder,如果要删除多个镜像,可以在命令中指定多个镜像名称或镜像 ID,用空格隔开:docker rmi thunderchen thunderchen2 123456 765222

当然我们还必须知道如何获取镜像名称/镜像ID,答案是docker images [OPTIONS] [REPOSITORY[:TAG]]OPTIONS 是可选的选项,可以用来指定查看镜像时的一些参数;REPOSITORY[:TAG] 是要查看的镜像名称或标签。查看所有镜像:

images

查看指定镜像:docker images thunderchen

查看标签为 latestthunderchen 镜像:docker images thunderchen:latest

现在我们已经了解了镜像容器的增删改查,离着我们的目标越来越近了

Docker Share

我们需要先注册Docker,和github一样,需要注册账号获取上传凭证。

步骤非常简单,直接略过了。

这是我的存储库:

my repositories

首先登陆自己账号: docker login -u thunderchen

按照右侧的指令直接push,是会报错的,缺少给项目添加tag标签:

docker tag thunderchen thunderchen/clearlove:version1.0

最后push,请注意上述图片中的红框命令,push后面必须是 thunderchen/clearlove,经过我的多次测试:docker tag thunderchen thunderchen/test:version1.0,更换“/”后面的字符,则不会在现有的repository中,会创建新的repository

new

官方建议的是周后 “/” 前面的字符必须是Docker用户的ID

docker push thunderchen/clearlove:version1.0

可见我们已经成功的推到远程仓库中。

Front-end deployment

拉取nginx镜像

docker pull nginx

pull latest版本

有些nginx的版本配置会不一样,我们需要进入到nginx的容器当中:

docker exec -it my-nginx /bin/bash

查看nginx目录:

ls /etc/nginx/

ls

找到默认配置文件:

cat /etc/nginx/conf.d/default.conf

OK了!

nice

在项目中创建default.conf文件

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    access_log  /var/log/nginx/host.access.log  main;
    error_log  /var/log/nginx/error.log  error;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

打开dockerFile文件,编写以下内容:

FROM nginx
COPY dist/ /user/share/nginx/html/
COPY  default.conf /etc/nginx/conf.d/default.conf

构建镜像

docker build -t thunderhub .

相关命令含义见上文

查看镜像

docker images thunderhub

运行容器

docker run -dp 3000:80 --name thunderhub  thunderhub

意思是将容器的80端口映射到主机的3000端口上,浏览器输入http://localhost:3000/即可访问

发布

略,见上文,此时此刻a bit tired

使用

再次描述一遍

  1. 首先在本地安装Docker。

  2. 打开终端或命令行,并输入以下命令以从Docker Hub上下载所需的镜像:docker pull <image_name>

  3. 等待镜像下载完成后,可以使用以下命令运行该镜像: -p 参数来进行端口映射。-v 参数来进行目录挂载。

    docker run -d --name my-app -p 8080:80 -v /path/to/local/folder:/container/folder -e MY_VAR=my_value my-image:latest
    

    my-image 镜像启动一个名为 my-app 的容器,并将容器内部的 80 端口映射到主机的 8080 端口上,将主机上的 /path/to/local/folder 目录挂载到容器的 /container/folder 目录上,同时设置了一个名为 MY_VAR 的环境变量,并将容器在后台运行。

    -e 参数来设置环境变量。--name 参数为容器设置一个自定义名称。-d 参数将容器在后台运行。

  4. 如果是私有库记得login。

附件

dockerfile中常见构建语法:

指令描述
FROM指定基础镜像,可以是官方仓库中的镜像,也可以是第三方仓库中的镜像,或者是自己构建的镜像
WORKDIR设置工作目录,用于后续指令的执行。
COPY复制文件或目录到镜像中的指定路径。
RUN在镜像中运行命令或脚本。
EXPOSE声明容器需要监听的端口。
ADD类似于COPY,但支持更多功能,例如解压缩压缩文件、自动下载文件等。
CMD指定容器启动时要运行的命令或脚本。
ENV设置环境变量,可以在后续指令中使用。
VOLUME声明容器需要挂载的卷。
USER设置运行容器时使用的用户。
HEALTHCHECK定义容器的健康检查方式。
ARG定义构建时传递给Dockerfile的参数。
ENTRYPOINT指定容器启动时要运行的主程序或脚本。

用的Docker命令:

命令描述举例
docker run启动一个新的容器。docker run -dp 3000:3000 thunderchen /docker run -it myapp bash
docker ps列出正在运行的容器。docker ps -a
docker images列出本地镜像。docker images thunderchen:latest
docker pull从Docker Hub拉取镜像。docker pull nginx
docker push将本地镜像推送到Docker Hub。docker push thunderchen/clearlove:version1.0
docker build根据Dockerfile构建新的镜像。docker build -t thunderchen .
docker stop停止运行中的容器。docker stop thunderchen
docker rm删除一个或多个容器。docker rm <容器名称/容器ID>
docker rmi删除一个或多个本地镜像。docker rmi thunderchen thunderchen2 123456 765222
docker inspect获取有关容器或镜像的详细信息。docker inspect thunderchen
docker exec在正在运行的容器中执行命令。docker exec clearlove ls
docker-compose使用Compose配置多个容器应用程序。
docker network使用Compose配置多个容器应用程序。

总结

本文总结甚是欠缺,对Docker的背景和延伸的Knowledge并没有阐述,包括虚拟机/容器等,甚至对Docker底层的实现以及优缺点没有阐述。因为镜像、容器等概念绝对不是Docker提出的。

当然还有一些docker的其他功能例如比较重要Docker Compose,下一篇文章中我会详细说明