Tianhe Gao

Docker

安装

按系统安装:

需要安装:Docker + Docker Compose

Docker Compose

  1. Ubuntu 20.04.4 LTS

    1# 多用户安装
    2mkdir -p /usr/local/lib/docker/cli-plugins
    3sudo curl -SL https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
    4sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
    5docker compose version
    6# Docker Compose version v2.2.3

    一直在用 Arch Linux,和 Arch Linux 相比,在 Ubuntu 上安装 Docker Compose 要繁琐一点(当前用户):

    1DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
    2mkdir -p $DOCKER_CONFIG/cli-plugins
    3curl -SL https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose
    4chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose
    5docker compose version
    6# Docker Compose version 2.3.3
    7sudo ln -s ~/.docker/cli-plugins/docker-compose /usr/bin/docker-compose
    8echo 'alias docker-compose="docker compose"' >> ~/.bashrc
  2. Arch

    1sudo pacman -S docker-compose # so easy!

    参考资料

    1. Compose V2 | Docker Documentation
    2. [[https://github.com/docker/docker-ce-packaging/pull/553#issuecomment-906294789]master] add docker-compose-plugin package (deb, rpm) by thaJeztah · Pull Request #553 · docker/docker-ce-packaging

配置

non-root 执行命令1

1sudo groupadd docker
2sudo usermod -aG docker $USER # 把 $USER 替换成当前用户
3## 登出再登陆,使更改生效。也可以运行以下命令激活改变
4newgrp docker
5## 验证可以没有 sudo 运行 docker
6docker run hello-world

开机启动 Docker2

1sudo systemctl enable docker.service
2sudo systemctl enable containerd.service

自定义 Docker 守护进程3

1# 开启 Docker 守护进程
2sudo systemctl start docker

通过修改 /etc/docker/daemon.json 文件进行自定义。这个文件能覆盖大部分 Docker 守护进程配置选项,不能通过它配置 HTTP 代理。

运行时目录和存储驱动:

1{
2    "data-root": "/mnt/docker-data",
3    "storage-driver": "overlay2"
4}

配置守护进程监听端口4

有两种方式,一种通过 daemon.json 一种通过 systemd 。只介绍第一种。

一、在 /etc/docker/daemon.json 中设置 hosts 连接到 UNIX 套接字和 IP 地址。

1{
2  "hosts": ["unix:///var/run/docker.sock", "tcp://127.0.0.1:2375"]
3}

二、重启 Docker

三、通过 netstat 命令检查更改生效与否

1sudo netstat -lntp | grep dockerd

在重启 Docker 这一步出错,通过 sudo systemctl restart docker.service 无法重启。再次阅读文档后,发现第一种方式适合不使用 systemd 的发行版。我的 Arch Linux 使用 systemd,所以无法通过第一种方式配置。

如果想要尝试这一种配置方法, systemd 会不断出现 docker.service: Start request repeated too quickly. 错误。

第二种配置方式:

1sudo vim /usr/lib/systemd/system/docker.service

找到 ExecStart 并按以下方式修改:

1- ExecStart=/usr/bin/dockerd -H fd://
2+ ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375

重载 systemctl 配置,重启 Docker:

1sudo systemctl daemon-reload
2sudo systemctl restart docker.service

此时检查端口,发现可行:

1sudo netstat -lntp | grep dockerd
2# tcp        0      0 127.0.0.1:2375          0.0.0.0:*               LISTEN      8823/dockerd

入门指南

什么是容器?

容器可以看作计算机的进程,但它与一般进程是隔离的。这种隔离策略使用了已经存在很多年的 Linux 内核的特性——命名空间5和控制组 cgroups6

所有的 container 其实都是在共享主机 Linux 的内核。

什么是容器镜像?

A container image represents binary data that encapsulates an application and all its software dependencies. Container images are executable software bundles that can run standalone and that make very well defined assumptions about their runtime environment.

– Kubernetes Documentation7

A container image is a static file with executable code that can create a container on a computing system. A container image is immutable—meaning it cannot be changed, and can be deployed consistently in any environment. It is a core component of a containerized architecture.

– Container Images: Architecture and Best Practices - Aqua8

镜像是二进制数据,它封装了应用运行所需的一切。

在运行镜像时,使用的是孤立系统,与主机隔离。

可以把容器视为 chroot 的扩展。文件系统来自镜像,但比 chroot 多了一层隔离。

什么是容器 volumes?

每次容器从镜像中构建时,都会是一个全新的开始,过去对旧有的容器做过的更改无法保存在新创建的容器上。当我们希望保存这些更改时,volumes 就出现了。它可以将容器的目标路径,挂载至主机系统中。当我们对当前容器中的文件进行修改时,这些修改会被保存至主机系统的特定 volume 中,即便当前容器被销毁,重新创建同样容器时,因为使用的还是之前的 volume,所以那些修改还在,也就达到了我们跨容器保存数据修改的目的。

volumes 有两种主要类型:named volumes 和 bind mounts。前者可以不必关心数据在主机的位置,但当我们想把主机的一些内容放到容器中时,named volumes 就无法达到目的。于是,bind mounts 就有了用武之地。它能把主机中的数据载入容器中,使得我们可以在容器中对数据进行操作。

多容器应用(TODO + MySQL)

一个容器是一个进程,最好只做一件事。

容器之间是互相隔离的,怎样才能通信呢?通过网络。 如果两个容器在相同网络环境下,它们便能互相通信;反之则不能。

以下是来自官方教程的命令(我修改了细节):

 1# 创建网络
 2docker network create todo-app
 3# 在已创建的网络下,创建数据库todos,并创建网络别名mysql
 4docker run -d \
 5     --network todo-app --network-alias mysql \
 6     -v todo-mysql-data:/var/lib/mysql \
 7     -e MYSQL_ROOT_PASSWORD=secret \
 8     -e MYSQL_DATABASE=todos \
 9     mysql:8.0
10# 检查todos是否创建成功
11docker exec -it <mysql-container-id> mysql -u root -p
12mysql> SHOW DATABASES;
13 +--------------------+
14 | Database           |
15 +--------------------+
16 | information_schema |
17 | mysql              |
18 | performance_schema |
19 | sys                |
20 | todos              |
21 +--------------------+
22 5 rows in set (0.00 sec)
23# 使用nicolaka/netshoot提供的dig命令检查mysql是否和todo应用在同一网络
24docker run -it --network todo-app nicolaka/netshoot
25dig mysql

注意:不要在生产环境中使用环境变量,更安全的做法是使用 .env 之类的文件9

使用 Docker Compose

在应用跟路径新建文件 docker-compose.yml

 1version: "3.7"
 2
 3services:
 4  app:
 5    image: node:12-alpine
 6    command: sh -c "yarn install && yarn run dev"
 7    ports:
 8      - 3000:3000
 9    working_dir: /app
10    volumes:
11      - ./:/app
12    environment:
13      MYSQL_HOST: mysql
14      MYSQL_USER: root
15      MYSQL_PASSWORD: secret
16      MYSQL_DB: todos
17
18  mysql:
19    image: mysql:8.0
20    volumes:
21      - todo-mysql-data:/var/lib/mysql
22    environment:
23      MYSQL_ROOT_PASSWORD: secret
24      MYSQL_DATABASE: todos
25
26volumes:
27  todo-mysql-data:

确保之前运行的容器都已经停止。

在当前应用根路径下运行,启动容器:

1docker-compose up -d

查看日志:

1docker-compose logs -f

全部停止:

1docker-compose down # 该命令不删除创建的 volumes
2docker-compose down --volumes # 该命令删除创建的volumes

安全检查

1docker scan image_name

常用命令

 1docker version # 输出Docker版本、系统等信息
 2
 3docker ps # 列出所有正在运行的容器
 4docker ps -a # 列出所有容器
 5docker build -t image_name . # 根据当前目录下的Dockerfile,构建镜像
 6docker run -dp 3000:3000 image_name # 后台运行image_name,本地端口3000,容器内端口也是3000
 7
 8## 在对image内容进行修改后,需要再次运行 docker build 以更新构建
 9docker stop container_name # 停止正在运行容器
10docker rm -f container_name # 移除正在运行容器
11docker rm container_name # 移除已停止容器
12
13## 发布自己的image
14docker push USER_NAME/image_name
15
16## 在容器内部执行命令
17docker exec <container-id> command
18
19## 管理镜像
20docker image
21docker image history image_name # 查看镜像层
22## 管理容器
23docker container
24
25## volume相关
26docker volume create volume_name # 创建一个 volume
27docker run -v volume_name:/container/path image_name # 连接 volume 至容器路径
28docker run -v "$(pwd):/container/path" image_name # 将主机所在的当前路径,放进容器的目标路径

分享一个比较冷门的 Dockerfile 的小技巧:

当你要安装一个 binary 工具时(比如 jq、yq、kubectl、helm、docker 等等),可以考虑直接从它们的镜像里 COPY 过来,替代使用 wget/curl 下载安装的方式,比如:

COPY –from=docker:20.10.12-dind-rootless /usr/local/bin/docker /usr/local/bin/docker

https://twitter.com/muzi_ii/status/1522599179918647296

— 参考资料

  1. https://docs.docker.com/get-started/

技巧

6 Docker Compose Best Practices for Dev and Prod

https://prod.releasehub.com/blog/6-docker-compose-best-practices-for-dev-and-prod

  1. for Dev

    • Mount Your Code as Volume to Avoid Unnecessary Rebuilds
    • Use an Override File
    • Use YAML Anchors
  2. for Prod

    • Leverage the Docker Restart Policy(updateconfig: true)
    • Correct Cleanup Order of Docker Images(Do not use docker rm -f as it may destroy useful images. Always run docker rm -f –remove-orphans.)
    • Setting Your Containers' CPU and Memory Limits

    Tip: If you want to run multiple containers with different memory limits on the same machine, ensure that all your containers have different memory limits. This is because each container views how much memory it needs.

应用

how to rm images

To remove Docker images that are not used by any containers, you can use the docker image prune command. This command allows you to clean up unused images effectively. Here's how to do it:

  1. Remove Dangling Images: By default, docker image prune will remove only dangling images, which are images that are not tagged and are not referenced by any container. You can run the following command:

    docker image prune
    
  2. Remove All Unused Images: If you want to remove all images that are not associated with any container (both dangling and unused images), you can use the -a flag:

    docker image prune -a
    

    This command will delete all images that do not have at least one container associated with them [2][3].

  3. Force Removal: If you want to bypass the confirmation prompt, you can add the -f or --force flag:

    docker image prune -a -f
    
  4. Prune Multiple Object Types: Alternatively, you can use the docker system prune command, which removes not only unused images but also stopped containers and unused networks. To remove everything, including unused images, you can run:

    docker system prune
    

    To include volumes in the cleanup, use:

    docker system prune --volumes
    

By using these commands, you can effectively manage and free up disk space by removing images that are not in use by any containers.


Learn more: 1. Docker – Removing Dangling and Unused Images - Baeldung 2. How to remove old and unused Docker images - Stack Overflow 3. Prune unused Docker objects | Docker Docs


No notes link to this note