Docker 基础入门
前言
在云计算和微服务架构日益盛行的今天,软件开发与部署的效率和灵活性成为了企业竞争力的关键因素之一。Docker,作为一种开源的容器化平台,凭借其轻量级、可移植性和易于管理的特性,迅速成为现代软件开发和运维领域的宠儿。本文主要总结一些 Docker 的基本概念、核心优势、应用场景以及记录如何使用 Docker 来优化软件开发与部署流程。
基本概念
Docker是一种开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源,它允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中,然后发布到任何支持 Docker 的平台上。这个容器包含了应用运行所需的所有组件,如代码、运行时库、系统工具和配置文件等,确保了应用在不同环境中的一致性和可靠性。
- 镜像(Image):Docker 镜像是一个只读模板,包含了创建 Docker 容器所需的指令集。它类似于一个轻量级的、独立的操作系统环境。
- 容器(Container):容器是镜像的运行实例。通过运行镜像,可以创建一个或多个容器,每个容器都是相互隔离的,但共享同一个操作系统的内核。
- 仓库(Registry):Docker 仓库用于存储和分发 Docker 镜像。Docker Hub是最知名的公共仓库之一,用户也可以搭建私有仓库来存储敏感或专有镜像。
核心优势
- 环境一致性:Docker 容器确保了开发、测试和生产环境的一致性,减少了 “在我机器上就能跑” 的问题。
- 资源隔离:容器之间彼此隔离,提高了系统的安全性和稳定性,即使一个容器出现问题,也不会影响到其他容器或宿主机。
- 高效利用资源:相比虚拟机,Docker 容器更加轻量级,因为它们共享宿主机的操作系统内核,启动速度更快,资源占用更少。
- 易于部署和扩展:通过 Docker Compose 和 Kubernetes 等工具,可以轻松实现应用的编排、部署和自动扩展。
- 跨平台兼容性:Docker 容器可以在任何支持 Docker 的操作系统上运行,无论是 Windows、Linux 还是 macOS,极大地提高了应用的可移植性。
应用场景
- 微服务架构:Docker 是实现微服务架构的理想工具,每个微服务可以作为一个独立的容器运行,便于管理和扩展。
- 持续集成/持续部署(CI/CD):Docker 简化了构建、测试和部署流程,使得自动化部署成为可能,加速了软件交付周期。
- 大数据处理:Docker 容器可以用于部署大数据处理框架,如Hadoop、Spark等,提高数据处理效率和灵活性。
- 开发环境管理:开发者可以使用 Docker 快速搭建开发环境,确保团队成员使用相同的环境配置,减少环境差异带来的问题。
- 边缘计算和物联网:Docker的轻量级和跨平台特性使其成为边缘计算和物联网设备部署的理想选择。
如何使用 Docker
安装Docker
sudo apt-get update
#移除老版本的docker
sudo apt-get remove docker docker-engine docker.io containerd runc
#安装HTTPS相关包
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
#添加Docker的官方GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
#验证指纹
sudo apt-key fingerprint 0EBFCD88
#设置稳定版repo
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
#安装Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
#验证安装成功
sudo docker run hello-world
设置非root账户去管理Docker
sudo groupadd docker
sudo usermod -aG docker $USER #$USER替换为ubuntu当前用户名
newgrp docker#验证非root账户运行docker hello-world容器
docker run hello-world
Docker 的基础操作
我们通过一个实例来熟悉下 docker 的基础操作。
镜像操作:
- 拉取镜像,比如说 Ubuntu 22.04 镜像:
docker pull ubuntu:22.04
- 通过 docker images 我们可以看到当前docker 的所有镜像,包括我们刚拉取的 ubuntu:22.04
docker images
- 通过 docker run 创建并启动一个新的 Ubuntu 22.04 容器实例,并以交互模式运行,分配一个伪终端。
docker run -it ubuntu:22.04 /bin/bash docker run -it --rm ubuntu:22.04 /bin/bash # 使用 --rm 在容器退出后自动删除容器# 使用 -v 将本机 ${HOME}/.ssh 挂载到容器 /home/syrius/.ssh,共享本机的 ssh 配置 docker run -it --rm -v ${HOME}/.ssh:/home/syrius/.ssh -v ${HOME}/.aws:/home/syrius/.aws ubuntu:22.04 /bin/bash# 使用 -u 指定用户,前提是镜像内部有这个用户,用户 id 需要在构建镜像时指定 docker run -it --rm -u 1000:1000 ubuntu:22.04 /bin/bash
- Docker 容器默认有一定的资源限制(如CPU、内存等)。如果需要,也可以通过 docker run 命令的参数来调整这些限制。
# 限制容器最多使用0.5个CPU核心 docker run --cpus=0.5 ...# 查看内存大小 free -h # 限制容器最多使用512MB的内存 docker run --memory=512m ...# 设置容器最多使用1GB的虚拟内存(内存+交换空间)总量,--memory-swap的值必须大于--memory的值 docker run --memory=512m --memory-swap=1g ...
容器操作:
- 查看所有容器
docker ps -a
- 将本机上的目录 lorawan/yocto_ws 复制到容器 <container_id> 根目录下的 lorawan 目录。(更好的方法是使用卷)
docker cp lorawan/yocto_ws <container_id>:/lorawan/
- 根据 container_id 进入特定容器
docker exec -it <container_name_or_id> /bin/bash
- 退出容器,直接在容器内执行 exit
exit
注意不要与 docker run 混淆,docker run 是根据镜像创建一个新的容器,每个容器之间的修改是独立的;通过 docker exec 可以进入已存在的容器去继续修改。
管理容器
Docker提供了一系列命令来管理容器,如查看容器列表(docker ps)、停止容器(docker stop)、删除容器(docker rm)等。
- 持久化存储:如果编译生成的文件需要在容器外部访问或持久化存储,可以使用 Docker 的卷(Volume)功能。
docker stats
,查看所有正在运行的容器的CPU和内存使用情况的实时更新。输出将包括容器的ID、名称、CPU使用率、内存使用量、内存限制、内存使用率、网络I/O、磁盘I/O以及进程数等信息。docker ps
,查看正在运行的容器,使用docker stop
,停止容器docker rm
,删除容器docker images
,查看本地镜像
使用 Docker Compose
-
Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具,通过编写 docker-compose.yml 文件,可以方便地管理多个容器的启动、停止和连接。一个简单的
docker-compose.yml
定义是这样的:version: '3.0' # 使用适当的 Compose 文件格式版本 services: dev: image: yocto_chirpstack:1.0.0 # 替换为你的镜像名称 volumes: - ~/.ssh:/home/syrius/.ssh # 挂载卷到容器内路径- ~/.gitconfig:/home/syrius/.gitconfig
- ~/.ssh:/home/syrius/.ssh:这个卷将主机上的 ~/.ssh 目录挂载到容器内的 /home/syrius/.ssh 目录。这允许容器访问主机的 SSH 密钥,以便容器可以执行需要 SSH 认证的操作(例如,从私有 Git 仓库拉取代码)。
- ~/.gitconfig:/home/syrius/.gitconfig:这个卷将主机上的 ~/.gitconfig 文件挂载到容器内的 /home/syrius/.gitconfig 文件。这允许容器使用主机的 Git 配置,包括用户名和电子邮件地址等身份信息。
-
执行特定的服务(在这个例子中是 dev 服务),启动一个 Bash shell,并且该服务容器在命令执行完毕后会被自动删除(由于 --rm 选项)。
docker-compose -f docker-compose.yaml run --rm dev
Docker 如何保存对容器/镜像的修改
docker images
查看要修改的镜像。
docker run -it ubuntu:22.04/bin/bash
以交互式方式启动镜像。- 进入容器后,修改镜像,比如修改镜像中已经部署的代码或者安装新的软件或包等。
- 修改完成之后,键入
exit
, 退出当前交互式容器。 docker ps -a
查看所有容器。docker commit <container_id> test:v1.0
保存对镜像容器的修改,新的镜像名称为 test,版本为 v1.0.0。
- 停止并删除相关容器(如果镜像正在被容器使用):
- 使用
docker stop <container_id>
来停止容器。 - 使用
docker rm <container_id>
来删除容器。
- 使用
- 删除镜像,镜像删除前需要先删除镜像的容器:
- 使用 docker rmi <image_id> 或 docker image rm <image_id> 来删除镜像。
- 也可以通过镜像名称来删除,例如docker rmi ubuntu:22.04(如果这是你要删除的镜像)。
构建镜像
创建 Dockerfile
Dockerfile 是一个文本文件,包含了构建 Docker 镜像所需的所有指令。通过编写 Dockerfile,可以定义镜像的基础镜像、需要安装的包、执行的命令等。比如说在基础 ubuntu22.04 镜像系统上搭建基于 yocto 的 lorawan chirpstack 协议栈的编译环境,Dockerfile 可以这么写:
# 使用官方的 Ubuntu 22.04 镜像作为基础镜像
FROM ubuntu:22.04 # 设置用户名全局变量
ENV USER_NAME=ubuntu# 定义时区为亚洲上海
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone# 安装一些必要软件
RUN apt-get update && \ apt-get install -y sudo gawk wget git diffstat unzip texinfo gcc build-essential chrpath \socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping \python3-git python3-jinja2 python3-subunit zstd liblz4-tool file locales libacl1 && \locale-gen en_US.UTF-8 && \pip3 install west # 创建一个新用户,并且配置所有 sudo 操作都不需要密码
RUN useradd -m -s /bin/bash ${USER_NAME} && echo 'syrius ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers# 复制文件到镜像中(这个操作是以 root 用户身份执行的)
COPY build/downloads /home/${USER_NAME}/lorawan/build/downloads# 修改目录用户组权限,要在 USER ${USER_NAME} 之前,否则无权限
RUN chown -R ${USER_NAME}:${USER_NAME} /home/${USER_NAME}/lorawan# 设置后续指令的执行用户为 ubuntu
USER ${USER_NAME} # 设置工作目录(这个目录将属于 root 用户,因为 COPY 指令是在 USER 指令之前执行的
WORKDIR /home/${USER_NAME}
- COPY 指令不受 USER 指令的直接影响。在 Dockerfile 中,COPY 指令用于将文件或目录从构建上下文(通常是 Dockerfile 所在的目录及其子目录)复制到镜像中指定的路径。这个操作是在 Docker 引擎构建镜像时执行的,而且默认是以 root 用户的身份执行的,无论 USER 指令是否已经在 Dockerfile 中出现。
- USER 指令用于设置后续指令(以及容器启动时默认的用户)的执行用户。这意味着,在 USER 指令之后的 RUN、CMD、ENTRYPOINT 等指令将以指定的用户身份执行。如果 USER 指令之后没有更多的指令,那么它主要影响的是容器启动时的默认用户。
使用 docker build 命令根据当前目录下的 Dockerfile 构建镜像。
docker build -t chirpstack:1.0.0 .
镜像上传
要将 Docker 镜像上传到 Docker Hub 的指定仓库,我们需要按照以下步骤操作:
- 登录到 Docker Hub,输入 Docker Hub 用户名和密码
当前中国境内已经无法直接访问 Docker Hub 了,所以这里可能会登录超时。需要配置 docker 代理1才行。
-
标记(Tag)镜像:
- 在上传镜像之前,需要给镜像打上一个标签(Tag),这个标签包括要上传到的 Docker Hub 用户名(或组织名)和仓库名,以及一个可选的标签(通常是版本号,如 latest)。
docker tag [本地镜像名]:[本地标签] [dockerhub用户名]/[仓库名]:[标签]# 例如,如果有一个名为 my-app 的镜像,并且你想将它上传到名为 my-repo 的仓库中,标签为 latest docker tag my-app:latest my-dockerhub-username/my-repo:latest
- 在上传镜像之前,需要给镜像打上一个标签(Tag),这个标签包括要上传到的 Docker Hub 用户名(或组织名)和仓库名,以及一个可选的标签(通常是版本号,如 latest)。
-
推送(Push)镜像,使用 docker push 命令将标记好的镜像推送到 Docker Hub。
docker push [dockerhub用户名]/[仓库名]:[标签]# 继续上面的例子: docker push my-dockerhub-username/my-repo:latest
-
验证上传:推送完成后,可以登录到 Docker Hub,并导航到仓库页面,确认镜像是否已成功上传。
以上步骤假设我们已经有一个 Docker Hub 账户,并且已经创建了一个仓库(虽然 Docker Hub 允许我们在推送镜像时自动创建仓库,但手动创建仓库可以提前设置好仓库的可见性和描述等信息)。
扩展
将 Docker 镜像推送到亚马逊 ECR 私有存储库
# 使用 AWS CLI 获取登录凭证
aws ecr get-login-password --region cn-northwest-1 | docker login --username AWS --password-stdin 633349536424.dkr.ecr.cn-northwest-1.amazonaws.com.cn# 列出 cn-northwest-1 区域下的所有 ECR 仓库
aws ecr describe-repositories --region cn-northwest-1# 列出 cn-northwest-1 区域下的所有 ECR 仓库的名称
aws ecr describe-repositories --region cn-northwest-1 | jq -r '.repositories[].repositoryName'# 查看是否存在 embedded/yocto 仓库
aws ecr describe-repositories --region cn-northwest-1 | jq -r '.repositories[].repositoryName' | grep "embedded/yocto"# 标记并推送镜像
# 633349536424.dkr.ecr.cn-northwest-1.amazonaws.com.cn 是远程镜像仓库的地址,这个地址指向AWS Elastic Container Registry(ECR)在中国(宁夏)区域(cn-northwest-1)的一个特定仓库。
# embedded/yocto 是远程仓库中的路径和仓库名称,用于组织和管理不同项目或团队的镜像。
docker tag chenqinhu/yocto:latest 633349536424.dkr.ecr.cn-northwest-1.amazonaws.com.cn/embedded/yocto:chirpstack-1.0.0
docker push 633349536424.dkr.ecr.cn-northwest-1.amazonaws.com.cn/embedded/yocto:chirpstack-1.0.0
配置 docker 代理
-
移除 Docker 守护进程配置文件,或配置文件里的代理配置(如果有的话),因为这里的配置会覆盖下面的配置。
sudo rm /etc/docker/daemon.json
-
创建配置文件
sudo mkdir -p /etc/systemd/system/docker.service.d sudo vim /etc/systemd/system/docker.service.d/http-proxy.conf
-
添加配置
[Service] Environment="HTTP_PROXY=http://127.0.0.1:1080" Environment="HTTPS_PROXY=http://127.0.0.1:1080" Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp,"
- IP 和端口需要根据自己的代理修改,比如我是在 wsl2 上的 ubuntu 使用的 docker,我的代理 IP和端口可以在 windows ,<控制面板> <网络和 Internet> <Internet 选项> <连接> <局域网设置> 里看到。
- 我们可以简单使用
curl -x 127.0.0.1:10809 www.google.com
来验证 wsl 里的 ubuntu 代理是走通的,不要使用 ping,ping 不通过代理。
- IP 和端口需要根据自己的代理修改,比如我是在 wsl2 上的 ubuntu 使用的 docker,我的代理 IP和端口可以在 windows ,<控制面板> <网络和 Internet> <Internet 选项> <连接> <局域网设置> 里看到。
-
重启容器
sudo systemctl daemon-reload sudo systemctl restart docker
-
查看代理是否生效
-
dockerhub 登录成功,说明 docker 的代理起作用了。
- ↩︎