Docker 基础用法

Posted by KalosAner on December 11, 2024

一、基本概念

Snipaste_2024-12-11_19-56-29

图片来源自:奇乐编程学院

Docker 就像一个轻量化的虚拟机,他可以创建多个虚拟的运行环境并在其中添加各种应用,这个环境在 Docker 中称之为容器(Container)。Linux 容器是 Linux 的一种虚拟化技术,但它不会模拟一个完整的操作系统,而是对进行进行隔离。Docker 的实现主要是对 Linux 容器的一种封装,它可以将应用程序和该程序的依赖打包在一个文件里。运行这个文件就会生成一个虚拟容器,程序在这个虚拟容器中运行。

二、安装 Docker

Docker 安装可以按照官网的教程安装,这里以 Ubuntu 为例简单介绍一下安装过程。

1、首先移除旧版本

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

2、设置 Docker 的 apt 仓库

我这里使用的是阿里的仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] http://mirrors.aliyun.com/docker-ce/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

注意:如果使用的是 Ubuntu 的衍生发行版,例如 Linux Mint,需要用 UBUNTU_CODENAME 替代 VERSION_CODENAME。官方教程中的 https://download.docker.com/linux/ubuntu/gpg 是Docker 官方的源,如果下载很慢可以和我一样使用阿里的源。

3、安装 Docker 包

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

4、docker 加速

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#启动 docker
systemctl enable docker --now
# 编辑 deamon.json 文件
sudo vim /etc/docker/daemon.json
# 输入以下内容
{
"registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://dockerproxy.com",
        "https://docker.mirrors.ustc.edu.cn",
        "https://docker.nju.edu.cn"
    ]
}

# 重启
sudo systemctl daemon-reload
sudo systemctl restart docker

其他镜像加速器地址:

镜像加速器地址 其他说明
https://dockerpull.com CF的workers来搭建
https://dockerproxy.cn CF的workers来搭建
https://docker.1panel.live 1Panel 面板提供(推荐)
https://hub.rat.dev 耗子面板提供
https://docker.chenby.cn Docker Hub
https://docker.anyhub.us.kg DockerHub 镜像加速代理
https://dockerhub.icu Docker镜像加速站
https://docker.ckyl.me Docker镜像加速站
https://dockerhub.jobcher.com Docker Hub
https://docker.hpcloud.cloud 镜像使用说明
https://docker.awsl9527.cn 镜像使用说明
https://www.hallodocker.com/ 镜像使用说明

5、创建 hello-world 检测 Docker 是否安装成功

1
2
3
4
5
6
# 检查 docker 启动状态
sudo systemctl status docker
# 如果服务未启动,则启动
sudo systemctl start docker
# 运行 hello-world 实例,如果本地未找到则会自动下载
sudo docker run hello-world

这个命令会下载一个测试镜像并且在容器中运行它,如果这个容器成功运行就会打印信息。

Snipaste_2024-12-12_14-59-51

注:官网还介绍了使用先下载包然后再安装的过程,可以自行查看。

配置 Docker 自启动

1
systemctl enable docker --now

重启 Docker

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

查看运行中的容器

1
docker ps

三、常用操作

帮助文档:<commend> --help,例如:docker images --help

以下很多操作都需要开代理,换镜像也很难生效,我也不知道怎么回事儿。

1、镜像操作

Docker 把应用程序及其依赖都打包在 image 文件里面,并且根据 image 文件生成的容器实例。

Docker 的 image 文件是通用的,一台机器的 image 文件拷贝到另一台机器可以照常使用。所以一般来说我们应该尽量使用别人制作好的 image 文件,或者用别人 image 文件进行加工,以节约时间。

1
2
3
4
5
6
7
检索镜像:docker search <image>

下载镜像:docker pull <image:version> (版本可以省略,默认:latest)

查看本地镜像:docker image ls

删除镜像:docker rmi <image:version>/<image id>

删除镜像必须先删除所有依赖这个镜像的容器。

2、容器操作

1
2
3
4
5
6
7
8
9
10
11
运行容器实例*:docker run <image:version> 		# version 可以省略默认使用最新镜像
查看运行中的容器:docker ps
查看所有容器实例:docker ps -a
停止容器:docker stop <name/id> 				# name 是 run 时随机生成的名字
启动Exited容器实例:docker start <name/id> 		# id 可以简写
重启容器实例:docker restart <name/id>			# 无论运行中还是退出的容器都可以使用
查看容器状态:docker stats <name/id>
查看日志:docker logs <name/id>
进入容器实例*:docker exec <name/id>
删除容器实例:docker rm <name/id>				# 只能删除退出的容器
强制删除容器实例:docker rm -f <name/id>

使用 docker run 运行的容器实例会阻塞,在另一个终端视图中使用 docker ps 查看运行中的容器可以看到刚才启动的容器实例的信息,包括 idnames 这两个都是自动分配的,可以用来对容器实例进行操作。在对容器实例进行操作时,使用 names 或者 id 都可以,并且 id 还可以简写,只要能与其他实例区分就行。

3、运行细节

本小节以 nginx 为例,nginx 容器实例启动后可以通过浏览器访问,访问方式是 IP:PORT,IP 就是自己的主机 IP,使用环回地址好像不行;PORT 就是容器实例的外部端口,默认就是 80,后边也可以自己指定容器的外部端口。

1、默认的 run 会在前台启动阻塞终端视图,并且容器的名字也是随机生成的。我们可以使用 run 加上参数设置容器在后台运行,并且指定名字。

1
docker run -d --name mynginx nginx

2、容器实例运行时会隔离端口,所以即便容器运行也无法从外部通过端口访问。我们可以使用 -p 80:88 来映射一个外部端口。

1
docker run -d --name mynginx -p 80:88 nginx

然后就可以使用自己的 IP:PORT 进行访问了。

3、每一个容器就像一个小的虚拟机,那么里边的文件也理应可以修改。我们可以使用 docker exec 命令来进入容器修改其中的内容。

1
2
3
4
# -it 指定以交互模式进入,/bin/bash 进入的方式
docker exec -it mynginx /bin/bash
# 退出
exit

4、保存镜像

1
2
3
提交镜像:docker commit -m "备注" <需要上传的image> <上传的名字:版本>
保存镜像:docker save -o <输出的文件名> <需要保存的镜像>
加载镜像:docker load -i <需要加载的镜像文件>

示例:

1
2
3
docker commit -m "update index.html" mynginx mynginx:v1.0
docker save -o mynginx.tar mynginx:v1.0		# -o 后加输出的文件名
docker load -i mynginx.tar

5、分享镜像

分享镜像需要先在 https://hub.docker.com/ 注册一个账号。

1
2
3
docker login
docker tag mynginx:v1.0 username/mynginx:v1.0	# 改名
docker push username/mynginx:v1.0	 			# 上传

然后进 https://hub.docker.com/ ,点自己的头像,点 my profile,点 repositories,然后就可以看到自己的镜像了。

四、进阶操作

1、优化命令

如果想要批量强制删除多个容器可以使用以下命令。

1
docker rm -f $(docker ps -aq)

docker ps -aq 是用来显示所有容器的 ID。使用 $ 符号可以把这个命令的值传递给前边的命令用来删除容器。

2、目录挂载

容器内部文件修改困难,而且修改之后删除容器再重新运行一个容器之前容器的数据无法再使用,这种情况可以使用目录挂载。目录挂载相当于使用外部主机的一个文件替代一个容器内部的文件。

1
docker run -d -p 80:80 -v /app/nghtml:/usr/share/nginx/html --name app01 nginx

这样容器运行时就会使用 nghtml 里边的文件替代 /usr/share/nginx/html,初始的时候 /app/nghtml 是空的,所以如果想要显示内容需要先在 nghtml 文件夹中创建 html 文件,这样的话 exec 进入到容器中查看 /usr/share/nginx/html 中的文件就会发现变成了和外部一样的文件。这样做的好处就是方便修改,而且当容器被删除时 nghtml 文件夹中的文件不会被删除。多个容器的文件可以挂载到同一个外部目录。

3、卷映射

目录挂载会使用外部的目录完全取代容器内部的文件,但是初始时外部目录默认是空的,所以有些场景不适用。卷映射相当于可以给容器内部的文件夹创建一个硬链接(但是文件夹是不能创建硬链接的,只是功能上类似),这样既可以在外部修改同时初始时又可以使用容器内原本的文件进行初始,而且当容器被删除时卷内的文件也不会被删除。

1
2
3
4
5
docker run -d -p 88:80 -v ngconf:/etc/nginx --name app02 nginx
# 创建的卷需要在 /var/lib/docker/volumes/<volume-name> 下找到
cd /var/lib/docker/volumes/ngconf
# 列举所有的卷
docker volume ls

4、docker 网络

有时候需要容器之间需要通信,本节介绍如何更轻松地找到其他容器的地址。docker 在启动时会创建一个虚拟网卡,docker 会为每一个容器分配一个 IP 地址。

首先可以通过 ip a 查看所有的网卡,就可以看到 docker0 的网卡。可以使用以下命令查看容器的详细信息,在最后边会有容器的网络信息,包括它的虚拟 IP 。

1
2
3
4
# 首先创建一个容器
docker run -d -p 88:80 --name app1 nginx
# 查看容器详细信息
docker container inspect app1

Snipaste_2024-12-15_15-54-53

可以看到它的虚拟 IP 是:172.17.0.2,然后其他容器就可以通过这个 IP 访问它。

在访问时可以直接通过 容器IP:容器内部端口 就可相互访问,如下。

1
curl http://172.17.0.2:80

但是容器的 IP 可以能会因为各种原因而发生变化,docker 支持自定义网络,使得同一个自定义网络中的所有容器可以通过主机名当域名的方式相互访问。

1
2
3
4
5
6
7
8
9
10
11
# 查看帮助文档
docker network --help
# 创建自定义网络
docker network create mynet
# 查看所有网络
docker network ls
# 将容器加入自定义网络
docker run -d -p 88:80 --name app1 --network mynet nginx
docker run -d -p 99:80 --name app2 --network mynet nginx
# 容器之间访问
curl http://app1:80

5、Redis 主从同步集群

Snipaste_2024-12-15_16-27-25

创建两个容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# mynet 是之间创建的自定义网络
# 创建容器1
docker run -d -p 6379:6379 \
-v /app/rd1:/bitnami/redis/data \
-e REDIS_REPLICATION_MODE=master \
-e REDIS_PASSWORD=123456 \
--network mynet --name redis01 \
bitnami/redis
# 如果容器启动失败可能是因为 /app/rd1 没有权限,可以修改以下这个文件夹的权限,-R 是递归修改rd1里边所有的文件夹
chmod -R 777 /app/rd1
docker restart redis01
# 创建容器2
mkdir /app/rd2
chmod -R 777 /app/rd2
docker run -d -p 6380:6379 \
-v /app/rd2:/bitnami/redis/data \
-e REDIS_REPLICATION_MODE=slave \
-e REDIS_MASTER_HOST=redis02 \
-e REDIS_MASTER_PORT_NUMBER=6379 \
-e REDIS_MASTER_PASSWORD=123456 \
-e REDIS_PASSWORD=123456 \
--network mynet --name redis02 \
bitnami/redis

五、Docker Compose

Docker Compose 官网教程:https://docs.docker.com/compose/

以 wordpress 为例:

原始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 启动 wordpress
docker run -d -p 8080:80 \
-e WORDPRESS_DB_HOST=mysql \
-e WORDPRESS_DB_USER=root \
-e WORDPRESS_DB_PASSWORD=123456 \
-e WORDPRESS_DB_NAME=wordpress \
-v wordpress:/var/www/html \
--restart always --name wordpress-app \
--network blog \
wordpress:latest
# 启动 mysql
docker run -d -p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=wordpress \
-v mysql-date:/var/lib/musql \
-v /app/myconf:/etc/mysql/conf.d \
--restart always --name mysql \
--network blog \
mysql:8.0

compose.yaml 配置示例:

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
30
31
32
33
34
35
36
37
38
39
40
41
# yaml 允许#作为注释,但是缩进不能使用Tab,必须使用空格
name: myblog
services:
  mysql:
  container_name: mysql # 这里给出了mysql容器名,如果不给的话会默认起名,如下边的wordpress
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment: # 数组写法
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_DATABASE=wordpress
    volumes:
      - mysql-data:/var/lib/mysql
      - /app/myconf:/etc/mysql/conf.d
    restart: always
    networks:
      - blog

  wordpress:
    image: wordpress
    ports:
      - "8080:80"
    environment: # KV写法
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_USER: root
      WORDPRESS_DB_PASSWORD: 123456
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress:/var/www/html
    restart: always
    networks:
      - blog
    depends_on:
      - mysql

volumes:
  mysql-data: # 卷的详细配置, 没有的话可以省略
  wordpress:

networks:
  blog:

然后使用以下名利进行启动:

1
docker compose -f compose.yaml up -d

注:这里要注意查看自己原来的 mysql 是不是开启状态,如果原来的 mysql 开启的话可能会因为端口占用导致容器启动失败。

compose.yaml 支持增量更新,直接在文件里修改配置然后重新执行:docker compose -f compose.yaml up -d 就行。

除了启动命令外还有其他名利如:

1
2
docker compose -f compose.yaml down  # 仅移除容器,可以加入参数对卷进行移除,查看 --help
docker compose -f compose.yaml down --rmi all -v # 既移除镜像又移除卷

还有其他的命令可以自行搜索。

六、制作镜像

Docker 制作镜像稍微有点繁琐,这里参考阮一峰的博客

1、下载项目源码

1
2
git clone https://github.com/ruanyf/koa-demos.git
cd koa-demos

2、编写 Dockerfile 文件

首先在项目的根目录下创建一个文件 .dockerignore,写入如下内容,表示下面三个路径(文件夹)不要打包进 image 文件。如果项目中没有需要排除的文件夹可以不建立这个文件。

1
2
3
.git
node_modules
npm-debug.log

然后创建 Dockerfile,写入如下内容。

1
2
3
4
5
FROM node:8.4
COPY . /app
WORKDIR /app
RUN npm install
EXPOSE 3000

上面代码一共五行,含义如下。

  • FROM node:8.4:该 image 文件继承官方的 node image,冒号表示标签,这里标签是8.4,即8.4版本的 node。
  • COPY . /app:将当前目录下的所有文件(除了.dockerignore排除的路径),都拷贝进入 image 文件的/app目录。
  • WORKDIR /app:指定接下来的工作路径为/app
  • RUN npm install:在/app目录下,运行npm install命令安装依赖。注意,安装后所有的依赖,都将打包进入 image 文件。
  • EXPOSE 3000:将容器 3000 端口暴露出来, 允许外部连接这个端口。

3、创建 image 文件

1
2
3
docker image build -t koa-demo .
# 或者
docker image build -t koa-demo:0.0.1 .

上面连个命令都可以用来创建 image 文件,-t 参数用来指定 image 文件的名字,后边可以用冒号指定标签(版本)。如果不指定标签默认就是 latest。最后的点表示 Dokerfile 文件所在的路径,. 表示当前路径。

如果运行成功就可以看到新生成的 image 文件。

1
docker image ls

Docker 制作镜像的内容有很多,可以边应用边学习,边参考Docker 制作镜像的官方文档

Instruction Description
ADD Add local or remote files and directories.
ARG Use build-time variables.
CMD Specify default commands.
COPY Copy files and directories.
ENTRYPOINT Specify default executable.
ENV Set environment variables.
EXPOSE Describe which ports your application is listening on.
FROM Create a new build stage from a base image.
HEALTHCHECK Check a container’s health on startup.
LABEL Add metadata to an image.
MAINTAINER Specify the author of an image.
ONBUILD Specify instructions for when the image is used in a build.
RUN Execute build commands.
SHELL Set the default shell of an image.
STOPSIGNAL Specify the system call signal for exiting a container.
USER Set user and group ID.
VOLUME Create volume mounts.
WORKDIR Change working directory.

4、运行容器

这个镜像需要通过 /bin/bash 运行。

1
2
3
docker container run -p 8000:3000 -it koa-demo:0.0.1 /bin/bash
# 然后运行 demos 中的 js
node demo/20.js

然后就可以通过浏览器 IP+Port 来访问了。

上面命令的各个参数含义如下:

  • -p参数:容器的 3000 端口映射到本机的 8000 端口。
  • -it参数:容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器。
  • koa-demo:0.0.1:image 文件的名字(如果有标签,还需要提供标签,默认是 latest 标签)。
  • /bin/bash:容器启动以后,内部第一个执行的命令。这里是启动 Bash,保证用户可以使用 Shell。

5、Docker 镜像分层机制

简单介绍以下 Docker 存储镜像的机制,Docker 用分层的机制存储镜像,这样可以提高复用性。同一个 image 所创建的容器在运行时共用一个 image 的数据(运行环境和运行的应用),每个容器只需要保存容器本身不同的数据。

container-layers

sharing-layers

参考:B站尚硅谷