docker 入門終極指南,詳細版!別再說不會用 docker 了!

docker 入門終極指南,詳細版!別再說不會用 docker 了!

富 web 時代,應用變得越來越強大,與此同時也越來越複雜。集群部署、隔離環境、灰度發布以及動態擴容缺一不可,而容器化則成為中間的必要橋樑。

最后更新 2023/8/15 下午8:39
Jartto's blog
预计阅读 16 分钟
分类
Docker
标签
Docker

Web 时代,应用变得越来越强大,与此同时也越来越复杂。集群部署、隔离环境、灰度发布以及动态扩容缺一不可,而容器化则成为中间的必要桥梁。

本节我们就来探索一下 Docker 的神秘世界,从零到一掌握 Docker 的基本原理与实践操作。别再守着前端那一亩三分地,是时候该开疆扩土了。

我們將會圍繞下面幾點展開:

  1. 講個故事
  2. 虛擬機與容器
  3. 認識 docker
  4. 核心概念
  5. 安裝 docker
  6. 快速開始
  7. 常規操作
  8. 最佳實踐

一、講個故事

为了更好的理解 Docker 是什么,我们先来讲个故事

我需要蓋一個房子,於是我搬石頭、砍木頭、畫圖紙、蓋房子。一頓操作,終於把這個房子蓋好了。

盖房子

結果,住了一段時間,心血來潮想搬到海邊去。這時候按以往的辦法,我只能去海邊,再次搬石頭、砍木頭、畫圖紙、蓋房子。

重复盖房

煩惱之際,跑來一個魔法師教會我一種魔法。這種魔法可以把我蓋好的房子複製一份,做成“鏡像”,放在我的背包里。

黑魔法

等我到了海邊,就用這個“鏡像”,複製一套房子,拎包入住。

是不是很神奇?对应到我们的项目中来,房子就是项目本身,镜像就是项目的复制,背包就是镜像仓库。如果要动态扩容,从仓库中取出项目镜像,随便复制就可以了。Build once,Run anywhere!

不用再關注版本、兼容、部署等問題,徹底解決了“上線即崩,無休止構建”的尷尬。

二、虛擬機與容器

開始之前,我們來做一些基礎知識的儲備:

  1. 虛擬機:虛擬化硬體

虚拟机 Virtual Machine 指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。在实体计算机中能够完成的工作在虚拟机中都能够实现。

在计算机中创建虚拟机时,需要将实体机的部分硬盘和内存容量作为虚拟机的硬盘和内存容量。每个虚拟机都有独立的 CMOS、硬盘和操作系统,可以像使用实体机一样对虚拟机进行操作。在容器技术之前,业界的网红是虚拟机。

虚拟机技术的代表,是 VMWareOpenStack。更多请参看百科虚拟机

  1. 容器:將作業系統層虛擬化,是一個標準的軟體單元

    • 隨處運行:容器可以將代碼與配置文件和相關依賴庫進行打包,從而確保在任何環境下的運行都是一致的。
    • 高資源利用率:容器提供進程級的隔離,因此可以更加精細地設置 cpu 和內存的使用率,進而更好地利用伺服器的計算資源。
    • 快速擴展:每個容器都可作為單獨的進程予以運行,並且可以共享底層作業系統的系統資源,這樣一來可以加快容器的啟動和停止效率。
  2. 區別與聯繫

    • 虚拟机虽然可以隔离出很多「子电脑」,但占用空间更大,启动更慢。虚拟机软件可能还要花钱,例如VMWare
    • 容器技術不需要虛擬出整個作業系統,只需要虛擬一個小規模的環境,類似“沙箱”;
    • 運行空間,虛擬機一般要幾 gb 到 幾十 gb 的空間,而容器只需要 mb 級甚至 kb 級;

我們來看一下對比數據:

特性 虛擬機 容器
隔離級別 作業系統級 進程
隔離策略 hypervisor(虛擬機監控器) cgroups(控制組群)
系統資源 5 ~ 15% 0 ~ 5%
啟動時間 分鐘級 秒級
鏡像存儲 GB - TB KB - MB
集群規模 上百 上萬
高可用策略 備份、容災、遷移 彈性、負載、動態

与虚拟机相比,容器更轻量且速度更快,因为它利用了 Linux 底层操作系统在隔离的环境中运行。虚拟机的 Hypervisor 创建了一个非常牢固的边界,以防止应用程序突破它,而容器的边界不那么强大。

物理機部署不能充分利用資源,造成資源浪費。虛擬機方式部署,虛擬機本身會占用大量資源,導致資源浪費,另外虛擬機性能也很差。而容器化部署比較靈活,且輕量級,性能較好。

虛擬機屬於虛擬化技術,而 docker 這樣的容器技術,屬於輕量級的虛擬化。

三、認識 docker

Docker

  1. 概念

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。

Docker 技术的三大核心概念,分别是:镜像 Image、容器 Container、仓库 Repository

  1. docker 輕量級的原因?

相信你也会有这样的疑惑:为什么 Docker 启动快?如何做到和宿主机共享内核?

当我们请求 Docker 运行容器时,Docker 会在计算机上设置一个资源隔离的环境。然后将打包的应用程序和关联的文件复制到 Namespace 内的文件系统中,此时环境的配置就完成了。之后 Docker 会执行我们预先指定的命令,运行应用程序。

鏡像不包含任何動態數據,其內容在構建之後也不會被改變。

四、核心概念

  1. Build, Ship and Run(搭建、运输、运行);
  2. Build once, Run anywhere(一次搭建,处处运行);
  3. Docker 本身并不是容器,它是创建容器的工具,是应用容器引擎;
  4. Docker 三大核心概念,分别是:镜像 Image,容器 Container、仓库 Repository
  5. Docker 技术使用 Linux 内核和内核功能(例如 Cgroupsnamespaces)来分隔进程,以便各进程相互独立运行。
  6. 由于 NamespaceCgroups 功能仅在 Linux 上可用,因此容器无法在其他操作系统上运行。那么 Docker 如何在 macOSWindows 上运行? Docker 实际上使用了一个技巧,并在非 Linux 操作系统上安装 Linux 虚拟机,然后在虚拟机内运行容器。
  7. 镜像是一个可执行包,其包含运行应用程序所需的代码、运行时、库、环境变量和配置文件,容器是镜像的运行时实例

更多关于 Docker 的原理,可以查看 Docker 工作原理及容器化简易指南,这里不再赘述。

五、安裝 docker

  1. 命令行安裝

HomebrewCask 已经支持 Docker for Mac,因此可以很方便的使用 Homebrew Cask 来进行安装,执行如下命令:

brew cask install docker

更多安装方式,请查看官方文档:安装 Docker

  1. 查看版本
docker -v
  1. 配置鏡像加速

设置 Docker Engine 写入配置:

{
  "registry-mirrors": [
    "http://hub-mirror.c.163.com/",
    "https://registry.docker-cn.com"
  ],
  "insecure-registries": [],
  "experimental": false,
  "debug": true
}
  1. 安裝桌面端

Docker 桌面端

桌面端操作非常简单,先去官网下载。通过 Docker 桌面端,我们可以方便的操作:

  1. clone:克隆一個項目
  2. build:打包鏡像
  3. run:運行實例
  4. share:共享鏡像

好了,準備工作就緒,下面可以大展身手了!

六、快速開始

安裝完 docker 之後,我們先打個實際項目的鏡像,邊學邊用。

1.首先需要大致了解一下我們將會用到的 11 個命令

命令 描述
FROM 基於哪個鏡像來實現
MAINTAINER 鏡像創建者
ENV 聲明環境變量
RUN 執行命令
ADD 添加宿主機文件到容器里,有需要解壓的文件會自動解壓
COPY 添加宿住機文件到容器里
WORKDIR 工作目錄
EXPOSE 容器內應用可使用的埠
CMD 容器啟動後所執行的程式,如果執行 docker run 後面跟啟動命令會被覆蓋掉
ENTRYPOINT 與 cmd 功能相同,但需 docker run 不會覆蓋,如果需要覆蓋可增加參數 -entrypoint 來覆蓋
VOLUME 數據卷,將宿主機的目錄映射到容器中的目錄
  1. 新建項目

为了快捷,我们直接使用 Vue 脚手架构建项目:

vue create docker-demo

嘗試啟動一下:

yarn serve

访问地址:http://localhost:8080/。项目就绪,我们接着为项目打包:

yarn build

这时候,项目目录下的 Dist 就是我们要部署的静态资源了,我们继续下一步。

需要注意:前端项目一般分两类,一类直接 Nginx 静态部署,一类需要启动 Node 服务。本节我们只考虑第一种。关于 Node 服务,下文我会详细说明。

  1. 新建 dockerfile
cd docker-demo && touch Dockerfile

此時的項目目錄如下:

.
├── Dockerfile
├── README.md
├── babel.config.js
├── dist
├── node_modules
├── package.json
├── public
├── src
└── yarn.lock

可以看到我们已经在 docker-demo 目录下成功创建了 Dockerfile 文件。

  1. 準備 nginx 鏡像

运行你的 Docker 桌面端,就会默认启动实例,我们在控制台拉取 Nginx 镜像:

docker pull nginx

控制台會出現如下信息:

Using default tag: latest
latest: Pulling from library/nginx
8559a31e96f4: Pull complete
8d69e59170f7: Pull complete
3f9f1ec1d262: Pull complete
d1f5ff4f210d: Pull complete
1e22bfa8652e: Pull complete
Digest: sha256:21f32f6c08406306d822a0e6e8b7dc81f53f336570e852e25fbe1e3e3d0d0133
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest

如果你出现这样的异常,请确认 Docker 实例是否正常运行。

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

镜像准备 OK,我们在根目录创建 Nginx 配置文件:

touch default.conf

寫入:

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    access_log  /var/log/nginx/host.access.log  main;
    error_log  /var/log/nginx/error.log  error;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
  1. 配置鏡像

打開 dockerfile ,寫入如下內容:

FROM nginx
COPY dist/ /usr/share/nginx/html/
COPY default.conf /etc/nginx/conf.d/default.conf

我們逐行解釋一下代碼:

  • FROM nginx 指定该镜像是基于 nginx:latest 镜像而构建的;
  • COPY dist/ /usr/share/nginx/html/ 命令的意思是将项目根目录下 dist 文件夹中的所有文件复制到镜像中 /usr/share/nginx/html/ 目录下;
  • COPY default.conf /etc/nginx/conf.d/default.confdefault.conf 复制到 etc/nginx/conf.d/default.conf,用本地的 default.conf 配置来替换 Nginx 镜像里的默认配置。
  1. 構建鏡像

Docker 通过 build 命令来构建镜像:

docker build -t jartto-docker-demo .

按照慣例,我們解釋一下上述代碼:

  • -t 参数给镜像命名 jartto-docker-demo
  • . 是基于当前目录的 Dockerfile 来构建镜像

執行成功後,將會輸出:

Sending build context to Docker daemon  115.4MB
Step 1/3 : FROM nginx
 ---> 2622e6cca7eb
Step 2/3 : COPY dist/ /usr/share/nginx/html/
 ---> Using cache
 ---> 82b31f98dce6
Step 3/3 : COPY default.conf /etc/nginx/conf.d/default.conf
 ---> 7df6efaf9592
Successfully built 7df6efaf9592
Successfully tagged jartto-docker-demo:latest

鏡像製作成功!我們來查看一下容器:

docker image ls | grep jartto-docker-demo

可以看到,我們打出了一個 133mb 的項目鏡像:

jartto-docker-demo latest 7df6efaf9592 About a minute ago 133MB

鏡像也有好壞之分,後續我們將居間如何優化,這裡可以先暫時忽略。

  1. 運行容器
docker run -d -p 3000:80 --name docker-vue jartto-docker-demo

這裡解釋一下參數:

  • -d 设置容器在后台运行
  • -p 表示端口映射,把本机的 3000 端口映射到 container80 端口(这样外网就能通过本机的 3000 端口访问了
  • --name 设置容器名 docker-vue
  • jartto-docker-demo 是我们上面构建的镜像名字

補充一點:

在控制台,我们可以通过 docker ps 查看刚运行的 ContainerID

docker ps -a

控制台會輸出:

CONTAINER ID IMAGE              COMMAND                  CREATED       STATUS PORTS  NAMES
ab1375befb0b jartto-docker-demo "/docker-entrypoint.…"   8 minutes ago Up 7 minutes  0.0.0.0:3000->80/tcp  docker-vue

如果你使用桌面端,那么打开 Docker Dashboard 就可以看到容器列表了,如下图:

Docker 桌面端

  1. 訪問項目

因为我们映射了本机 3000 端口,所以执行:

curl -v -i localhost:3000

或者打开浏览器,访问:localhost:3000

  1. 發布鏡像

如果你想為社區貢獻力量,那麼需要將鏡像發布,方便其他開發者使用。

發布鏡像需要如下步驟:

  • 登录 dockerhub,注册账号;
  • 命令行执行 docker login,之后输入我们的账号密码,进行登录;
  • 推送镜像之前,需要打一个 Tag,执行 docker tag <image> <username>/<repository>:<tag>

全流程结束,以后我们要使用,再也不需要「搬石头、砍木头、画图纸、盖房子」了,拎包入住。这也是 docker 独特魅力所在

七、常規操作

到這裡,恭喜你已經完成了 docker 的入門項目!如果還想繼續深入,不妨接著往下看看。

  1. 參數使用
  • FROM

    • 指定基础镜像,所有构建的镜像都必须有一个基础镜像,且 FROM 命令必须是 Dockerfile 的第一个命令
    • FROM <image> [AS <name>] 指定从一个镜像构建起一个新的镜像名字
    • FROM <image>[:<tag>] [AS <name>] 指定镜像的版本 Tag
    • 示例:FROM mysql:5.0 AS database
  • MAINTAINER

    • 鏡像維護人的信息
    • MAINTAINER <name>
    • 示例:MAINTAINER Jartto Jartto@qq.com
  • RUN

    • 構建鏡像時要執行的命令
    • RUN <command>
    • 示例:RUN ["executable", "param1", "param2"]
  • ADD

    • 將本地的文件添加複製到容器中去,壓縮包會解壓,可以訪問網絡上的文件,會自動下載
    • ADD <src> <dest>
    • 示例:ADD *.js /app 添加 js 文件到容器中的 app 目录下
  • COPY

    • 功能和 ADD 一样,只是复制,不会解压或者下载文件
  • CMD

    • 启动容器后执行的命令,和 RUN 不一样,RUN 是在构建镜像是要运行的命令
    • 当使用 docker run 运行容器的时候,这个可以在命令行被覆盖
    • 示例:CMD ["executable", "param1", "param2"]
  • ENTRYPOINT

    • 也是执行命令,和 CMD 一样,只是这个命令不会被命令行覆盖
    • ENTRYPOINT ["executable", "param1", "param2"]
    • 示例:ENTRYPOINT ["donnet", "myapp.dll"]
  • label:為鏡像添加元數據,key-value 形式

    • LABEL <key>=<value> <key>=<value> ...
    • 示例:LABEL version="1.0" description="这是一个web应用"
  • env:設置環境變量,有些容器運行時會需要某些環境變量

    • ENV <key> <value> 一次设置一个环境变量
    • ENV <key>=<value> <key>=<value> <key>=<value> 设置多个环境变量
    • 示例:ENV JAVA_HOME /usr/java1.8/
  • expose:暴露對外的埠(容器內部程式的埠,雖然會和宿主機的一樣,但是其實是兩個埠)

    • EXPOSE <port>
    • 示例:EXPOSE 80
    • 容器运行时,需要用 -p 映射外部端口才能访问到容器内的端口
  • volume:指定數據持久化的目錄,官方語言叫做掛載

    • VOLUME /var/log 指定容器中需要被挂载的目录,会把这个目录映射到宿主机的一个随机目录上,实现数据的持久化和同步。
    • VOLUME ["/var/log","/var/test".....] 指定容器中多个需要被挂载的目录,会把这些目录映射到宿主机的多个随机目录上,实现数据的持久化和同步
    • VOLUME /var/data var/log 指定容器中的 var/log 目录挂载到宿主机上的 /var/data 目录,这种形式可以手动指定宿主机上的目录
  • workdir:設置工作目錄,設置之後 ,run、cmd、copy、add 的工作目錄都會同步變更

    • WORKDIR <path>
    • 示例:WORKDIR /app/test
  • user:指定運行命令時所使用的用戶,為了安全和權限起見,根據要執行的命令選擇不同用戶

    • USER <user>:[<group>]
    • 示例:USER test
  • arg:設置構建鏡像是要傳遞的參數

    • ARG <name>[=<value>]
    • ARG name=sss

更多操作,请移步官方使用文档

八、最佳實踐

在掌握 docker 常規操作之後,我們很容易就可以打出自己想要的項目鏡像。然而不同的操作打出的鏡像也是千差萬別。

究竟是什麼原因導致鏡像差異,我們不妨繼續探索。

以下是在應用 docker 過程中整理的最佳實踐,請儘量遵循如下準則:

  1. require 明確:需要什麼鏡像
  2. 步驟精簡:變化較少的 step 優先
  3. 版本明確:鏡像命名明確
  4. 說明文檔:整個鏡像打包步驟可以重現

推薦如下兩篇文章:

九、總結

容器化技術必將是雲時代不可或缺的技能之一,而 docker 只是滄海一粟。隨之而來的還有集群容器管理 k8s、service mesh 、istio 等技術。打開 docker 的大門,不斷抽絲剝繭,逐層深入,你將感受到容器化的無窮魅力。

趕快打開技能邊界,為你的前端技術賦能吧!

版權聲明: 文章首发于 Jartto's Blog , 转载文章请务必以超链接形式标明文章出处、作者信息及本版权声明。

Keep Exploring

延伸阅读

更多文章
同标签 2025/2/25

net 10 preview 1發布

今天.net 10 preview 1發布了,我第一時間下載,升級了avalonia ui項目和博客網站,前者功能測試及aot發布正常,後者調試正常,docker暫時未成功

继续阅读