[toc] # Docker vs VM ![docker ](https://i.imgur.com/2CFPuDY.png) | 特性 | 說明 | | -------- | -------- | | 靈活性 | 複雜的應用程式也可以容器化 | | 輕量化 | 容器共享內核,系統消耗比虛擬機器低 | | 可移植 | 可在本地建構,部署到任何地方運行 | | 低耦合 | 容器間相依低,更換或者升級不會影響其他容器 | | 可擴展 | 可以在數據中心跑多個容器 | | 安全性 | 容器之間預設的網路是隔離的 | | 特性 | Docker | VM | | -------- | -------- | --- | | 啟動速度 | 秒級 | 分鐘級 | | 硬碟使用 | 一般為MB | 一般為GB | | 性能 | 接近原生 | 較差 | | 系統支援量 | 單機支持上千容器 | 一般幾十個 | | 隔離性 | 安全隔離 | 完全隔離 | # 基本概念 * Image 映像檔 * Image是一個模板,用來產生容器 * 映像檔可以包含完整的服務、編譯環境、或是作業系統 * Container 容器 * 容器是用映像檔建立出來的實體 * 它可以被啟動、開始、停止、刪除 * 每個容器都是相互隔離。 * Repository 倉庫 * 倉庫是映像檔存放的地方 * 倉庫可分為公開倉庫和私有倉庫 * Docker Hub是官方公開倉庫,存放了許多官方維護的映像檔供使用者下載 # install ```bash= winget install -e --id Docker.DockerDesktop brew install --cask docker sudo apt-get install docker.io ``` # docker命令 ![docker commands diagram](https://i.imgur.com/BssPo3z.png) ```bash= docker info docker version docker images docker rmi $(docker images -f dangling=true -qa) docker rmi $(docker images -qa) # 以nginx為例 docker pull nginx:1.21.6-alpine # 生命週期 watch docker ps -a docker run -d --restart=always --name w1 -p 8080:80 nginx:1.21.6-alpine docker logs -f w1 docker stop w1 docker start w1 docker restart w1 docker pause w1 docker unpause w1 docker kill w1 docker rm w1 # 匯出匯入 watch docker images docker run -i -t --rm --name w2 -p 8080:80 nginx:1.21.6-alpine docker exec -i -t w2 sh docker commit -m 'edit index' w2 docker cp w2:/usr/share/nginx/html/index.html . docker cp ./index.html w2:/usr/share/nginx/html docker export w2 > w2.tar docker import ./w2.tar abc123:0.0.1 docker save abc123:0.0.1 > abc123.tar docker rmi abc123:0.0.1 docker load < abc123.tar docker stop w2 docker rmi nginx:1.21.6-alpine docker tag abc123:0.0.1 nginx:1.21.6-alpine ``` # Dockerfile ![docker build and run](https://i.imgur.com/ZXgmvzp.png) ```yaml= # 基底映像檔 FROM ubuntu:20.04 as base # 影像檔資訊 # LABEL maintainer="abc@example.com" # LABEL description='abc123' # 指定執行容器的user USER node # 容器編譯時期執行的命令 RUN apt-get update \ && apt-get upgrade -y \ # 工作目錄 WORKDIR /app # 容器執行時期的環境變數 ENV TYPEORM_HOST=127.0.0.1 ENV TYPEORM_USERNAME=usr ENV TYPEORM_DATABASE=pwd # 編譯時用 --build-arg 帶入的參數 # 容器編譯時期的變數 # ARG # 複製本機的資料至容器內 COPY dist /app/dist # 複製網路的資料,並自動解壓縮到容器內 # ADD https://www.example.com/abc.tar /app/lib/ # 建立一個可以對外掛載的目錄 # VOLUME # 宣告此容器Listen的Port號 EXPOSE 3000 # 指定容器啟動程序及參數 # ENTRYPOINT ["entrypoint.sh"] # 容器啟動時執行的命令 CMD ["node", "dist/main"] ``` ```bash= docker build -t abc:123 . docker build -t abc:456 -f dev.dockerfile . docker tag abc:123 xyz:456 ``` ```bash= FROM busybox ENTRYPOINT ["ping"] CMD ["168.95.1.1"] ``` # Understand how CMD and ENTRYPOINT interact [Understand how CMD and ENTRYPOINT interact](https://docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact) ## Node Js Dockerfile ```dockerfile= FROM node:lts-alpine as base RUN mkdir -p /app && chown -R node:node /app USER node WORKDIR /app ENV NODE_ENV production ENV TYPEORM_CONNECTION mysql ENV TYPEORM_HOST localhost ENV TYPEORM_PORT 3306 ENV TYPEORM_USERNAME dev ENV TYPEORM_PASSWORD dev ENV TYPEORM_DATABASE ezpay ENV TYPEORM_LOGGING true ENV TYPEORM_ENTITIES node_modules/@ezpay/database/dist/entity/*.js COPY --chown=node:node dist /app/dist COPY --chown=node:node .npmrc package.json /app/ RUN npm install EXPOSE 3000 CMD ["node", "dist/main"] ``` ## .Net Core Dockerfile ```dockerfile= FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env WORKDIR /app # Copy everything COPY . ./ # Restore as distinct layers RUN dotnet restore # Build and publish a release RUN dotnet publish -c Release -o out # Build runtime image FROM mcr.microsoft.com/dotnet/aspnet:6.0 WORKDIR /app COPY --from=build-env /app/out . ENTRYPOINT ["dotnet", "DotNet.Docker.dll"] ``` # docker compose ```yaml= version: '3.5' networks: local: name: local services: db: container_name: 'mysql' image: 'mysql:8.0.29' command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci'] restart: always environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: db01 MYSQL_USER: dev MYSQL_PASSWORD: dev ports: - 3306:3306 networks: - local adminer: container_name: 'adminer' image: 'adminer:4.8.1' restart: always ports: - 8080:8080 networks: - local ``` ```bash= docker-compose up docker-compose up -d docker-compose down docker-compose start docker-compose stop docker-compose restart docker-compose build docker-compose ps docker-compose top docker-compose images docker-compose config ``` # Makefile ``` DIRNAME:= $(notdir $(CURDIR)) .PHONY: build build: docker build -t $(DIRNAME):local . run: docker run -i -t --rm -p 3000:3000 $(DIRNAME):local rmi: docker rmi $(DIRNAME):local clean: rm -rf coverage rm -rf node_modules rm -rf dist rm -f junit.xml rm -f package-lock.json ``` # Container Registry [CNCF Container Registry](https://landscape.cncf.io/card-mode?category=container-registry&grouping=category) * Harbor * Portus * Nexus Repository * Docker Hub * Google Container Registry * Azuer Container Registry * Amazon Elastic Container Registry