Docker

更适合前端宝宝的 Docker 教程

CentOS 中安装 docker
Windows 中安装 docker

docker hub
使用 docker bashWindows 配置:需要先开启 hyper-V 或手动追加环境变量 PATH 路径 C:\Program Files\Docker\Docker\resources\bin重启后生效

Dockerfile 用法

docker 可以理解成一个集装箱,里面有各种服务和环境。不必考虑里面装了什么,只在要有 docker 的主机上,就可以运行 docker 镜像,移植性强。

比如:你要跑一个陌生语言的服务,该服务如果提供 docker 镜像的话,就不用在自己本地捣鼓该服务的环境了,直接 pull 该服务镜像让其在本机 docker 的容器中运行即可。

Docker 技术的三大核心概念

镜像 Image、容器 Container、仓库 Repository

<font style="color:rgb(37, 41, 51);">image</font>: 镜像,可以理解为一个容器的模板,通过一个镜像可以创建多个容器

<font style="color:rgb(37, 41, 51);">container</font>: 最小型的一个操作系统环境(虚拟机),可以对各种服务以及应用容器化,是镜像的运行实例

<font style="color:rgb(37, 41, 51);">registry</font>: 镜像仓库,存储大量镜像,可以从镜像仓库拉取和推送镜像

基础使用

linux 中 启动 docker => sudo 表示   允许普通用户使用 root 的超级权限以及命令的工具。

1
sudo systemctl start docker

查看下载的 docker 镜像

1
docker image

搜索 镜像资源

1
docker search 镜像关键字

拉取镜像

1
docker pull 镜像name

查看容器

1
2
3
4
# 查看运行的容器
docker ps
# 查看所有容器
docker ps -a

宿主机和容器之间复制文件或目录 cp

1
2
docker cp <源地址> <目标地址>
docker cp nginx1:/usr/share/nginx/html ~/nginx-htm

资源管理

1
2
3
4
5
6
7
8
9
docker port #查看容器端口映射
docker logs -f container_name #查看实时日志
docker stats #查看容器资源占用
docker exec -it <container-name> bash #进入容器bash
docker inspect <容器id> #查看容器的详情
docker rm <容器名称>
docker rmi <镜像标识符> #删除镜像
docker image rm <镜像名称> #删除镜像
docker stop [...容器名称]

发布镜像到 docker hub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

## 构建镜像
$ docker build -t <image-name:可跟版本号> <Dockerfile路径>
$ docker build -t docker-demo-vue .

## 构建镜像 (指定Dockerfile文件名)
$ docker build -t <image-name:可跟版本号> -f <指定Dockerfile文件名> <Dockerfile路径>
$ docker build -t docker-demo-vue -f as.Dockerfile .

## login
$ docker login

## 打上tag
$ docker tag <image> <username>/<repository-ame>:<tag>
$ docker tag docker-vue bazijun/docker-vue:1.0

## push
$ docker push <username>/<repository-name>:<tag>
$ docker push bazijun/docker-vue:1.0

docker 中 启动容器

先构建镜像

1
docker build -t <image-name:可跟版本号/标签> <Dockerfile路径>

启动!

1
2
docker run -d -p 8080:80 --name my-nginx nginx
docker run -d -p 3306:3306 -v /Users/guang/mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql-container mysql

-d (deamon) 即后台运行

--rm 当停止容器时自动清楚容器

-p 端口映射 => 本机端口:容器端口

-v 指定挂载卷 => 本机路径:容器路径

<font style="color:rgb(37, 41, 51);">-e</font> 指定环境变量

<font style="color:rgb(37, 41, 51);">--name</font> 设置容器名

<font style="color:rgb(37, 41, 51);">--network</font> 指定 network namespace

最后的是镜像名

关闭服务

1
docker stop 容器名/容器id

dockerfile 例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# FROM node:18 是继承 node:18 基础镜像。
FROM node:18

# WORKDIR /app 是指定当前目录为 /app
WORKDIR /app

# COPY 复制宿主机的 package.json 和 lock 文件到容器的当前目录,也就是 /app 下
COPY package.json .

COPY *.lock .

RUN npm config set registry https://registry.npmmirror.com/

# RUN 是执行命令,这里执行了 npm install。
RUN npm install

COPY . .

RUN npm run build

# EXPOSE 指定容器需要暴露的端口是 3000。
EXPOSE 3000

# 指定默认挂载卷
VOLUME /app

# CMD 指定容器跑起来时执行的命令是 node ./dist/main.js。
CMD [ "node", "./dist/main.js" ]

这里有个细节就是,docker 是分层存储的,dockerfile 里的每一行指令是一层,会做缓存。每次 docker build 的时候,只会从变化的层开始重新构建,没变的层会直接复用。所以上面示例中 只是 <font style="color:rgb(37, 41, 51);">COPY package.json . </font>后才执行 <font style="color:rgb(37, 41, 51);">npm install</font>, 而非是在COPY整个目录后执行。为的就是只有在package.json变化后才重新装包。

关键字

  • **<font style="color:rgb(37, 41, 51);">FROM</font>**:基于一个基础镜像来修改
  • **<font style="color:rgb(37, 41, 51);">WORKDIR</font>**:指定当前工作目录
  • **<font style="color:rgb(37, 41, 51);">COPY</font>**:把容器外的内容复制到容器内
  • **<font style="color:rgb(37, 41, 51);">EXPOSE</font>**:声明当前容器要访问的网络端口,比如这里起服务会用到 8080
  • **<font style="color:rgb(37, 41, 51);">RUN</font>**:在容器内,docker build 时执行的命令
  • **<font style="color:rgb(37, 41, 51);">CMD</font>**容器启动即 docker run 时执行的命令
  • **<font style="color:rgb(37, 41, 51);">VOLUME</font>** 指令看起来没啥用,但能保证你所指定的容器内那个目录下的数据一定会被持久化,能保证没挂载数据卷的时候,数据不丢失。(未指定挂载卷时会随机生成一个目录作为数据卷挂载上去

细节

  • 使用 **<font style="color:rgb(37, 41, 51);">alpine</font>** 的镜像,而不是默认的 linux 镜像,可以极大减小镜像体积,比如 <font style="color:rgb(37, 41, 51);">node:18-alpine3.14</font><font style="color:rgb(37, 41, 51);">node:lts-alpine3.19</font> 这种
  • 不同容器可以使用相同的<font style="color:rgb(37, 41, 51);">EXPOSE</font>端口,但是每个容器在主机上的映射端口必须是唯一的。否则,启动容器时会出现端口冲突错误。
  • 使用多阶段构建,比如一个阶段来执行 build,一个阶段把文件复制过去,跑起服务来,最后只保留最后一个阶段的镜像。这样使镜像内只保留运行需要的文件以及 dependencies。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# build stage
FROM node:18 as build-stage

WORKDIR /app

COPY package.json .

RUN npm config set registry https://registry.npmmirror.com/

RUN npm install

COPY . .

RUN npm run build

# production stage
FROM node:18 as production-stage

COPY --from=build-stage /app/dist /app
COPY --from=build-stage /app/package.json /app/package.json

WORKDIR /app

RUN npm install --production

EXPOSE 3000

CMD ["node", "/app/main.js"]

  • 使用 ARG 增加构建灵活性,ARG 可以在 docker build 时通过 –build-arg xxx=yyy 传入,在 dockerfile 中生效,可以使构建过程更灵活。如果是想定义运行时可以访问的变量,可以通过 ENV 定义环境变量,值使用 ARG 传入。
  • <font style="color:rgb(37, 41, 51);">CMD</font><font style="color:rgb(37, 41, 51);">ENTRYPOINT</font> 都可以指定容器跑起来之后运行的命令,CMD 可以被覆盖,而 ENTRYPOINT 不可以,两者结合使用可以实现参数默认值的功能。
  • <font style="color:rgb(37, 41, 51);">ADD</font><font style="color:rgb(37, 41, 51);">COPY</font> 都可以复制文件到容器内,但是 ADD 处理 tar.gz 的时候,还会做一下解压。
  • docker build 的时候会把 dockerfile 和它的构建上下文(也就是所在目录)打包发送给 docker daemon (docker 守护程序) 来构建镜像。可以通过 <font style="color:rgb(37, 41, 51);">.dockerignore</font> (语法和 .gitignore 一致) 指定哪些文件不发送,这样能加快构建时间,减小镜像体积。
1
2
3
4
5
6
7
8
9
10
11
12
*.md
!README.md
node_modules/
[a-c].txt
.git/
.DS_Store
.vscode/
.dockerignore
.eslintignore
.eslintrc
.prettierrc
.prettierignore

原理

Docker 实现原理的三大基础技术:

  • <font style="color:rgb(37, 41, 51);">Namespace</font>:实现各种资源的隔离
    • PID namespace: 进程 id 的命名空间
    • IPC namespace: 进程通信的命名空间
    • Mount namespace:文件系统挂载的命名空间
    • Network namespace:网络的命名空间
    • User namespace:用户和用户组的命名空间
    • UTS namespace:主机名和域名的命名空间
  • <font style="color:rgb(37, 41, 51);">Control Group</font>:实现容器进程的资源访问限制
    • 比如 cpu 用多少、内存用多少、磁盘用多少,然后加到这个组里的进程就会受到这个限制。
  • <font style="color:rgb(37, 41, 51);">UnionFS</font>:实现容器文件系统的分层存储,相同层可复用,镜像合并
    • dockerfile 描述镜像构建的过程,每一条指令都是一个镜像层。
    • 镜像通过 docker run 就可以跑起来,对外提供服务,这时会添加一个<font style="color:rgb(37, 41, 51);">可写层(容器层</font>。挂载一个 volume 数据卷到 Docker 容器,就可以实现数据的持久化。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!