前言
近几年,基本上大多数互联网公司都已经使用Docker来进行项目打包上线了,因此学会Docker不言而喻是一个前端开发者也必须掌握的技能了.但是工作中我虽然用的多,但是掌握的也不够透彻,因此我花了半个多月的时间仔仔细细的梳理了一遍,我总结了工作中最常用的一些知识点,希望对大家有帮助,如有遗漏或者错误,欢迎大家来指正~~
(要是点个赞就更感激不尽了)
安装
- PC: docs.docker.com/docker-for-…
- Mac OS: docs.docker.com/docker-for-…
- Linux:
curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh
如果安装不上可以用这个
curl -sSL https://get.daocloud.io/docker | sh
启动docker
systemctl start docker
如果大家使用的编辑器是vscode
可以安装一下Docker的vscode
插件
Docker的基本操作
// 启动
systemctl start docker
// 开机默认启动
systemctl enable docker
// 查看版本
docker version
// 查看信息
docker info
// 查看用法
docker
容器
什么是容器?对于我自己的理解,可以认为在编程语言中对象的概念,镜像可以看做一个类class
,它可以new
很多对象,容器就相当于类的对象,每一次run
都创建出一个新的对象
容器的操作
// 容器相关命令
docker container *
// 创建容器并启动(如果没有镜像则会拉取镜像再创建容器)
docker container run nginx
// 创建容器并启动,当停止容器时自动删除
docker container run --rm nginx
// eg: 创建mysql容器启动
// --name 设置容器名
// -e设置环境变量
// -d 后台运行
// -p 端口映射,宿主机端口:容器内端口
// mysql:tag 具体哪个镜像例如mysql:8.0.27, 不设置tag默认最新版本
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 -d mysql:tag
// 启动容器
docker container start 容器ID/容器NAME
// 查看正在运行的容器
docker container ls
// 停止容器
docker container stop 容器NAME/容器ID(这个ID可以就写前面几位,只要能区分出来就好)
docker container stop 0b1
// 批量停止容器(写多个容器ID)
docker container stop 0b1 258 2b7
// 批量停止全部容器(两个命令相结合 docker container ls -aq 获取所有容器的ID)
docker container stop $(docker container ls -aq)
// 查看所有容器(已退出/正在运行)
docker container ls -a
// 删除容器
docker container rm 容器NAME/容器ID(这个ID可以就写前面几位,只要能区分出来就好)
// 批量删除全部容器(与停止同理)
docker container rm $(docker container ls -aq)
// 或者
docker system prune -f
// 强制删除容器(正在运行的容器不能删除,需要强制删除)
docker container rm -f 容器NAME/容器ID
其实也有docker container ps
的命令,它和docker container ls
用法几乎相同,现在最新版都是采用ls
的命令了,当然以前的也可以用
上面我在执行run
的时候可以发现,我们的命令行是执行中的(Mac OS/Linux)下,如果通过CTRL + C是会停止容器,因此如果需要在后台执行-d
-p
为端口映射,宿主机访问8080会映射到容器中的80端口
// 后台执行
docker container run -d -p 8080:80 nginx
查看后台运行的nginx日志(不推荐)
docker attach 容器ID
另外一种查看日志的方式(动态查看日志-f
)
docker container logs -f 容器ID/容器NAME
这里多说一句,不管是镜像ID还是容器ID不需要全部写上,只要能区分开容器ID之间的区别即可
容器的交互
之前使用nginx镜像,镜像中其实为我们已经设置了command,如果我们想在镜像中进行命令操作的话,以ubuntu举例,以下命令就可以进入到镜像中
// 创建容器并进入容器中
docker container run -it ubuntu 执行的命令
docker container run -it ubuntu sh
// 进入已经运行的容器中(退出也不会停止容器运行)
docker container exec -it 容器ID 执行的命令
docker exec -it 容器ID 执行的命令
docker exec -it 0b1 sh
// 查看容器的进程
docker container top 容器ID
镜像
镜像我们好比做一个提前声明好的类,用它来创建容器
获取镜像
- Docker Hub 获取
- Dockerfile构建
- 文件导入
镜像的操作
// 查看镜像操作
docker image
// 第一种获取方式: Docker Hub 获取
// 拉取镜像(默认拉取latest)
docker image pull nginx
// 拉取指定版本镜像
docker image pull nginx:TAGS
docker image pull nginx:1.21.4
// 查看镜像列表
docker image ls
// 查看镜像详细信息
docker image inspect 镜像ID
// 删除镜像(如果容器正在使用的镜像则无法删除,停止容器了也不行)
docker image rm 镜像ID
// 删除全部镜像
docker image rm $(docker image ls -q)
// 强制删除全部镜像
docker image rm -f $(docker image ls -q)
// 或者
docker image prune -a
// 第二种获取方式: 文件导入
// 打包成镜像文件(-o为导出 )
docker image save nginx -o nginx.image
// 导入(-i为导入)
docker image load -i ./nginx.image
// 查看镜像构建历史
docker image history 镜像NAME/镜像ID
Dockerfile构建镜像
首先创建两个文件
// 第三种: Dockerfile
FROM ubuntu:latest
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y python3.9 python3-pip python3.9-dev
ADD test.py /
CMD ["python3","test.py"]
// test.py
print("hello docker")
// 构建 -t 命名 :版本,默认为latest .当前目录
docker image build -t sayhello .
docker image build -t sayhello:1.0.0 .
// 指定构建文件进行构建 -f 指定文件
docker image build -f ./Dockerfile-test -t sayhello:1.0.0 .
// 运行
docker container run sayhello
发布镜像
发布是有要求的 命名为: 用户ID/镜像名:版本
// 修改tag (id代表你的docker hub 的用户名)
docker image tag sayhello id/sayhello:1.0.0
docker image tag sayhello xiaoming/sayhello:1.0.0
// 登录
docker login
// 发布
docker image push id/sayhello:1.0.0
// 拉取(注意把本地的此镜像删除)
docker image pull id/sayhello:1.0.0
Dockerfile
通过Dockerfile构建镜像,首先要设置一个你当前镜像的一个基础镜像,就有点像前端我们打算写一个React组件,我们首先要:
import React form 'react';
同理,构建一个镜像,也和写一个组件差不多,在这里,我想多说两句,比如有的镜像如nginx,ubuntu,mysql等等,那nginx是怎么运行的呢?其实不管是什么镜像,底层都是linux,只不过这个linux很小,只保留了你这个程序所需要的一些功能,那么我们如何选择基础镜像,有三个原则
- 有官方尽量选官方的
- latest不一定最好,选择带有版本号的,这样比较稳定
- 尽量选择体积小的镜像,如带有alpine tag的镜像
使用
RUN
每执行一次RUN就会为这个镜像生成一层,为了减少层数,尽量放在一个RUN指令中
RUN npm run build && \
npm run uploadOss
COPY & ADD
将本地文件复制到容器中,如果未找到目录,则会创建这个目录
COPY index.html /app/index.html
两个命令的区别在于ADD可以在复制的同时还会解压缩文件
WORKDIR
相当于cd 到某个目录,如果这个目录不存在,会自动创建,还会影响后面的操作目录,会将index.html
复制到/home
中
WORKDIR /home
COPY index.html index.html
EXPOSE
在当前容器中暴露一个端口给宿主机,它只是起到一个解释说明的作用,具体端口如何配置在后面会有介绍
ARG
通过ARG指令创建一个变量
注意定义变量时=
不能有空格
ARG PYTHON_VERSION=3.9
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y python${PYTHON_VERSION} python3-pip python${PYTHON_VERSION}-dev
也可以只定义,不赋值,后面通过命令的形式赋值
docker image build -t hello-python --build-arg PYTHON_VERSION=3.9
ENV
设置一个环境变量
ARG PYTHON_VERSION=3.9
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y python${PYTHON_VERSION} python3-pip python${PYTHON_VERSION}-dev
设置的环境变量会保存至镜像内部,创建容器时可以使用
CMD
定义容器启动时执行什么命令,CMD只能设置一个,设置多个也只会执行最后一个命令,并且可以通过docker image container run
进行覆盖
CMD ["npm", "run", "dev"]
ENTRYPOINT
ENTRYPOINT
一定能够执行,不会被其他命令所覆盖,并且ENTRYPOINT
和ENV
可以一起使用
ENTRYPOINT ["echo"]
CMD []
docker container run -it test hello world
输出hello world
创建一个node镜像
这里以koa工程为例
mkdir koa-example && cd koa-example
npm init -y
npm i koa
touch server.js Dockerfile .dockerignore
下面我们通过Dockerfile构建镜像
// server.js
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
// Dockerfile
FROM node:16.13.1-alpine3.14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
// 复制当前目录文件到容器
COPY . .
EXPOSE 3000
CMD [ "node", "server.js" ]
这里可能会疑惑的是为什么package.json要单独COPY
,因为如果放在一起的话,由于RUN
是分层的,修改了这一层的代码我们就要重新的去构建一次,因此需要分开写
我们要把最有可能修改的那一层放在最后,docker在构建镜像时,是可以使用缓存的,但是如果我们再最上面一层被修改,即使下面没有发生变化,也将不会使用缓存构建镜像
// .dockerignore docker忽略COPY的文件
node_modules
npm-debug.log
docker image build -t koa-demo .
docker container run -d -p 3000:3000 koa-demo
上面的例子中也用到了.dockerfile
,它与.gitignore
是相似的,.gitignore
不会被上传到orgin
,而.dockerfile
将文件中设置的文件忽略,不会被COPY
最后访问localhost:3000
我们熟悉的Hello World
是不是出来了呢👌
存储
在使用容器中,避免不了我们要保存一些数据,但是如果这个容器被删除,那么这个容器中的数据也会被一并删掉,但是很多情况下,比如数据库的一些数据或者日志,我们需要被保存下来,我们就需要一种方案将它们存储下来
有两种方案进行存储:
- Data Volume
- Bind Mount
VOLUME
这里用到了VOLUME
,通过Dockerfile
中设置VOLUME
来保证数据的持久化
FROM node:16.13.1-alpine3.14
WORKDIR /app
COPY . .
VOLUME ["/app"]
CMD [ "node", "index.js" ]
在这个Dockerfile
中设置了VOLUME
,这个目录下的文件讲不会因容器的删除而删除了数据
我们可以通过命令查看所有的VOLUME
docker volume ls
查看详细信息
docker volume inspect 7786b04eb9bbb8f40266139be70f0f53477c2c0fb5c388632f67abadc903eb23
但是!这里会有一个问题,虽然这样把数据存储下来,但是如果我们重新的创建容器,我们又会生成一个新的volume
,我们想每一次创建容器沿用之前的volume
,因此我们需要指定一个volume
命名
docker container run -d -v volume-name:/app volume-demo
这样设置命名后,即使再创建了容器,只要指定了volume
名,就会使用这个volume
Bind Mount
可以映射一个宿主机的路径(windows使用${pwd})
不需要设置VOLUME
.
bind mount的作用是将宿主机路径的文件映射至容器中,容器可以进行文件一系列操作,但是容器所操作的文件其实就是宿主机被映射的文件,和VOLUME
所不同的是,VOLUME
指定以后会先生成出来一个VOLUME
供你使用,但是它是被Docker内部所维护的
docker container run -it -v $(pwd):/app node
网络
宿主机与容器
对于docker的网络是如何传输,需要对网络协议有一定的了解.
首先,容器之间是可以通信的,容器和宿主机之间也是可以通信的,首先我们可以命令查看一下
docker network ls
我们可以看到有一个bridge
,就是通过桥的方式进行连接的.也就是说docker为我们创建了一个局域网环境
docker network inspect 183
我们可以看到Gateway
是172.17.0.1
下面我们创建一个nginx容器并进入其中的一个容器,访问一下宿主机
docker pull nginx:1.20.2-alpine
docker container run -d --name nginx1 nginx:1.20.2-alpine
docker container run -d --name nginx2 nginx:1.20.2-alpine
docker exec -it nginx1 sh
并且我们查询一下ip
// 宿主机查询容器ip
docker container inspect --format '{{.NetworkSettings.IPAddress}}' containerName
我们可以看到分配了一个局域网的ip地址.并且所有的容器都会分配一个局域网ip,它不仅仅可以访问宿主机,也可以访问其他的容器,另一个容器也就是172.17.0.3
,可以启动另一个容器尝试一下
端口映射
上面我们谈到我们在docker的局域网内,那么如果宿主机的局域网下,其他设备想访问当前宿主机的docker容器那又怎么办呢
// 创建一个容器并进行端口映射
docker container run -d -p 80:80 --name nginx2 nginx:1.20.2-alpine
此时当我们访问127.0.0.1
时访问的就是我们容器的nginx了
也就是说当访问我们的宿主机时192.168.31.27:80
,此时会通过NAT
转换为docker的局域网ip,再通过端口映射到nginx容器.
docker不仅仅有bridge
模式,还有host模式
// 查看docker网络模式
docker network ls
host相当于与宿主机共享了网络,不需要再进行端口映射,通过host配置网络的容器,访问宿主机就是访问此容器
docker container run -d --name nginx4 --network host nginx:1.20.2-alpine
此时正在运行的容器POSTS没有端口转发了,某种意义来说,省去了端口转发,提升了性能
这里说一下,mac下设置host很有可能会失效,host模式可以在linux下使用
docker-compose
docker-compose是一个帮助开发者Docker使用的工具,它帮助我们进行以一些docker的命令操作,而不需要我们自己通过命令执行 比如如果我们想运行一个nginx容器
docker container run -d --name nginx1 nginx
而使用docker-compose
// docker-compose.yml
version: "3.8"
services:
nginx-service:
image: nginx
ports:
- 80:80
这样通过一个文件来执行命令,是不是方便多了
安装
- Windows和Mac在安装Docker时docker-compose就自动安装了
- Linux安装如下:
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
使用
docker-compose语法的相关说明: 文档
由于docker-compose是使用的yaml的文件类型,如果不熟悉的朋友可以读一下阮一峰老师的yaml语言教程: yaml语法说明
下面是一个docker-compose的一个简易说明:
// docker-compose.yml
version: "3.8" docker-compose版本
services: # 容器,可以定义多个
servicename: # 服务名字,对应命令参数--name,这个名字也是内部 bridge网络可以使用的 DNS name
image: # 镜像名
command: # 可选,如果设置,则会覆盖默认镜像里的 CMD命令
environment: # 可选,相当于 docker run里的 --env
volumes: # 可选,相当于docker run里的 -v
networks: # 可选,相当于 docker run里的 --network
ports: # 可选,相当于 docker run里的 -p
servicename2:
volumes: # 可选,相当于 docker volume create
networks: # 可选,相当于 docker network create
对docker-compose进行命令操作要在docker-compose.yml
的当前目录下,且文件名是docker-compose.yml
相关命令如下:
// 查看docker-compose命令
docker-compose
// 启动
docker-compose up
// 后台启动与container同理
docker-compose up -d
// 停止
docker-compose stop
// 查看列表
docker-compose ps
docker-compose
创建的容器名是有规律的: 当前docker-compose
所在的文件夹名 + serviceName + 数字,比如prd_ngnixServer_1
,其实也可以修改,但是不推荐,会有一些问题,因此我们在使用docker-compose
的时候尽量先把项目的文件夹命名提前起好
自定义
在配置镜像时,采用的是官方镜像,如果我们的一些配置在Dockerfile
,那再拿过来又比较麻烦,因此,我们可以自定义镜像
services: # 容器,可以定义多个
node-demo:
build: ./node # ./node/Dockerfile node是工程目录,目录中要有Dockerfile这个文件
image: flask-demo:latest # 自定义镜像名,build要在image之前
举例
// mongo
// docker-compose.yml
version: "3"
services:
mongo:
image: mongo
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
ports:
- 27017:27017
volumes:
- /home/mongo:/data/db
// redis
// docker-compose.yml
version: "3"
services:
redis-test:
image: redis
restart: always
container_name: "redis-test"
ports:
- 6379:6379
volumes:
- /home/redistest:/data
command: [ 'redis-server', "--requirepass", "111111" ]
拉取
可以通过提前拉取准备好镜像
docker-compose pull
Docker镜像加速
有的小伙伴在使用docker拉取镜像会特别的慢,一个镜像可能需要很久的时间,这里我给大家推荐两种方案
镜像加速器
这个适用于你购买了云服务器,但是这种办法最好,省心省事,你只需要按着官方配置就行,这里以阿里云举例
登录阿里云容器Hub
这里面列举了各种Linux版本的配置方法,直接命令复制粘贴,里面的加速器地址都给你生成好了,是不是很easy~
Docker-ce
访问相关链接按照文档操作,这种方案适用于我们自己本机配置使用
至此,Docker的基本知识也写的差不多了,这篇文章也是我边学边总结的一篇文章,如有遗漏或者错误,欢迎指正.