# Docker 社課 [TOC] --- ## 自我介紹 - 名字: 李緒成 - 動機: 對 docker 不熟, 所以想準備 docker - 聯絡資訊: - https://www.linkedin.com/in/peterxcli - https://github.com/peterxcli - https://www.facebook.com/peterxcli ---- ![image](https://hackmd.io/_uploads/Bkrkam5ET.png) --- how can we handle this? ![image](https://hackmd.io/_uploads/BJFBc-t46.png) --- ## Introduction to Docker ---- ### Containerization and its benefits ---- #### Consistency Across Environments: Containers are consistent across different environments ![1_Ibnwjo9LtUFxRY1MZgOcvg](https://hackmd.io/_uploads/B15fyE5E6.jpg =300x) ---- #### Efficiency Containers share the machine’s OS system kernel and therefore do not require an OS per application, driving higher server efficiencies and reducing server and licensing costs. ---- #### Speed Containers can reduce the time it takes to spin up an app's operating system and begin running the application to mere seconds. ---- #### Isolation Containers isolate application processes from the rest of the system, reducing the risk of system conflicts. ---- #### Modularity and Scalability The container model encourages dividing the application into its functional components which can be independently scaled and managed. ---- #### Rapid Deployment and Scaling Containers include the application and all of its dependencies. ![image](https://hackmd.io/_uploads/S1ajp-FEa.png) ---- ### Docker's role in the development ---- #### Development Docker provides a consistent environment for application development and testing, reducing discrepancies and bugs. ---- #### Integration and Testing Automated Docker containers can build and test environments quickly and scale to simulate production environments. ---- #### Deployment Docker ensures that the application operates in any environment by bundling it with all its dependencies. ---- #### Maintenance and Update Containers can be easily stopped, modified, and restarted for maintenance and updates, thus simplifying the process and minimizing downtime. --- ### Comparison with traditional virtualization ---- ![image](https://hackmd.io/_uploads/BJfW1fFE6.png) ---- #### Underlying Technology | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Technology | Container runtime on host OS kernel. | Hypervisors managing full virtual machines with separate kernels. | ---- #### Resource Efficiency | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Efficiency | Shares OS kernel, minimal overhead. | Requires full guest OS, more resource-intensive. | ---- #### Startup Time | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Startup Speed | Starts in seconds. | Takes minutes to boot a full OS. | ---- #### Application Packaging | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Packaging | Packages applications with dependencies in images. | VMs include OS, application, and all dependencies. | ---- #### Isolation | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Level of Isolation | Process-level, using namespaces and cgroups. | Hardware-level, each VM operates independently. | ---- #### Portability | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Portability | Highly portable across different environments. | Less portable, often tied to hypervisor formats. | ---- #### Density | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Workload Density | Higher density due to lower overhead. | Lower density due to overhead of multiple OS instances. | ---- #### Performance | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Performance | Near-native, direct access to host kernel. | Reduced by hypervisor overhead. | ---- #### Security | Aspect | Container | Traditional Virtualization | |--------|-------------------------|----------------------------| | Security | Less isolated, depends on host OS security. | More isolated, advantageous for sensitive applications. | ---- #### Docker 為什麼會比傳統 VM 輕量? <!-- 因為少跑了 N 個完整的 Guest OS,下面就是 Virtualization 和 Containerization 的比較表: --> | | Virtualization | Containerization | | ------------- | --------------- | ---------------- | | 啟動 | 慢 (最快也要分鐘) | 快 (秒開) | | 容量 | 大 (GB) | 小 (MB) | | 效能 | 慢 | 快 | | host 可支撐數量 | 數個~數十個 | 數個~數百個 | | 複製相同環境 | 超慢 | 快 | ---- ![image](https://hackmd.io/_uploads/Bk6O-X9Ep.png) 因為少跑了 $N$ 個完整的 Guest OS --- ## Docker Fundamentals ---- ### Docker Architecture docker is a client-server model architecture ![image](https://hackmd.io/_uploads/SkT5pot4T.png =460x) ---- ![image](https://hackmd.io/_uploads/SyQwssFNT.png) ---- #### **Docker Daemon** The server-side service that manages Docker objects such as `images`, `containers`, `networks`, and `volumes`. It’s the engine that runs on the host machine and responds to requests from the Docker client. ---- #### **Docker Client** The command-line interface (CLI) that users interact with. The Docker client sends commands to the Docker daemon, which carries them out. ---- #### **Docker Objects** ---- ##### **Images** A Docker image is built up from a series of layers. Each layer represents an instruction in the image's Dockerfile. Each layer except the very last one is read-only. - image:多個 R/O layer (只讀層,read-only),它們重疊在一起。除了最下面一層,其它層都會有一個指標指向下一層 ---- ![image](https://hackmd.io/_uploads/ByRy8pF4a.png) ---- ###### what is layer? Each layer contains two main components: - **Files**: contains the files and directories that were created, modified, or deleted in that layer. - **Metadata**: contains information such as the command that was run to create the layer, environmental changes, user changes, exposed ports, and volume information. ---- ```dockerfile= # syntax=docker/dockerfile:1 FROM ubuntu:22.04 LABEL org.opencontainers.image.authors="org@example.com" COPY . /app RUN make /app RUN rm -r $HOME/.cache CMD python /app/app.py ``` ---- - Commands that create a layer: 1. `FROM ubuntu:22.04` (line 3) : Starts with the Ubuntu image, creating a base layer. 2. `COPY . /app` (line 5) : Copies local files into the image, creating a new layer. 3. `RUN make /app` (line 6) : Builds the application, creating a new layer. 4. `RUN rm -r $HOME/.cache` (line 7) : Removes the cache directory, creating a new layer. ---- - Commands that only modify metadata: 1. `LABEL <key>=<value>` (line 4) : Sets the image's author label, no new layer. 2. `CMD python /app/app.py` (line 8) : Sets the default command to run in the container, no new layer. ---- - Optmizing build Each layer is only a set of differences from the layer before it. **Note that both adding, and removing files will result in a new layer**. In the example above, the `$HOME/.cache` directory is removed, but will still be available in the previous layer and add up to the image's total size. Refer to the [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) and use [multi-stage builds](https://docs.docker.com/build/building/multi-stage/) sections to learn how to optimize your Dockerfiles for efficient images. ---- ##### The copy-on-write (CoW) strategy ```shell $ docker image history acme/my-base-image:1.0 IMAGE CREATED CREATED BY SIZE COMMENT da3cf8df55ee 5 minutes ago RUN /bin/sh -c apk add --no-cache bash # bui… 2.15MB buildkit.dockerfile.v0 <missing> 7 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 7 weeks ago /bin/sh -c #(nop) ADD file:f278386b0cef68136… 5.6MB ``` ```shell $ docker image history acme/my-final-image:1.0 IMAGE CREATED CREATED BY SIZE COMMENT 8bd85c42fa7f 3 minutes ago CMD ["/bin/sh" "-c" "/app/hello.sh"] 0B buildkit.dockerfile.v0 <missing> 3 minutes ago RUN /bin/sh -c chmod +x /app/hello.sh # buil… 39B buildkit.dockerfile.v0 <missing> 3 minutes ago COPY . /app # buildkit 222B buildkit.dockerfile.v0 <missing> 4 minutes ago RUN /bin/sh -c apk add --no-cache bash # bui… 2.15MB buildkit.dockerfile.v0 <missing> 7 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 7 weeks ago /bin/sh -c #(nop) ADD file:f278386b0cef68136… 5.6MB ``` ---- ##### [BuildKit](https://docs.docker.com/build/buildkit/) - Detect and skip executing unused build stages - Parallelize building independent build stages - Incrementally transfer only the changed files in your build context between builds - Detect and skip transferring unused files in your build context - Use Dockerfile frontend implementations with many new features - Avoid side effects with rest of the API (intermediate images and containers) - Prioritize your build cache for automatic pruning ---- ##### **Containers** ![image](https://hackmd.io/_uploads/By7jspYET.png) ---- - container:也是分層的,當執行一個 container 時,會在 image 的最上面加一層 R/W layer (可讀寫層) container = image (layer) + R/W layer (container layer) 對正在執行的 container 所做的所有更改 (如寫入新,修改和刪除檔案) 都會寫入此可寫的 container layer 當 container 被刪除時,可寫層也會被刪除,但底層 image 保持不變 ---- ##### **Networks** ---- ###### 1. Default Bridge Network Containers on the default bridge network can only access each other by IP addresses, unless you use the `--link` option ---- ![image](https://hackmd.io/_uploads/HyEXHRY4T.png) ---- docker 會新增一個 software bridge 作為 container 網路對外的出口,預設名稱為 docker0 docker0 會與 host 中的對外網卡(上圖為 eth0)相連,藉此取得對外連線的能力 每個 container 會使用一個 veth device 與 docker0 相連,因此具備連外能力 ---- ###### 2. user-defined Bridge Network more isolated and flexible ---- ```shell $ docker network create --driver bridge my-bridge ``` ```shell $ docker run -d --name app3 --network my-bridge nginx:alpine $ docker run -d --name app4 --network my-bridge nginx:alpine $ docker run -d --name app2 nginx:alpine ``` ---- Try to reach app4 from app3 using the hostname app4: ```shell $ docker exec app3 curl app4 # succeess ``` Try to reach app3 from app4: ```shell $ docker exec app4 curl app3 # succeess ``` Will we be able to reach app2 from app3 using DNS? ```shell $ docker exec app3 curl app2 # failed ``` ---- ###### 3. Host Network 此模式的網路有以下特點: - 不使用獨立的網路,而是與 Docker Host 使用相同的網路 - container 如果沒有對外開 port 提供服務,其實設定為 host mode 並沒有意義 ---- 網路架構如下圖: ![image](https://hackmd.io/_uploads/rJ3UmRtN6.png) ---- ##### 4. more but I didnt research - overlay - IPvlan - Macblan - None ---- ##### **Volumes** ---- ![image](https://hackmd.io/_uploads/Sy5VPMqVp.png) Volumes is managed by Docker (located at `/var/lib/docker/volumes/` on linux) ---- ###### 1. volumes - **Create a volume:** ```shell $ docker volume create my_volume ``` - **List volumes:** ```shell $ docker volume ls ``` ---- - **Run a container with a volume:** This command runs a container and mounts the volume `my_volume` to the container at the path `/data`. ```shell $ docker run -d --name my_container -v my_volume:/data my_image ``` - **Inspect a volume:** ```shell $ docker volume inspect my_volume ``` - **Remove a volume:** ```shell $ docker volume rm my_volume ``` ---- ###### 2. bind mounts Bind mounts may be stored anywhere on the host system. They may even be important system files or directories. ---- - **Run a container with a bind mount:** This command mounts the host directory `/path/on/host` to the container at the path `/path/in/container`. ```shell $ docker run -d --name my_container -v /path/on/host:/path/in/container my_image ``` ---- ##### **`rw` vs. `ro` in docker volume** - Read-Only (`ro`) ```shell # volume: $ docker run -d --name my_container -v my_volume:/data:ro my_image # bind mounts: $ docker run -d \ --name my_container \ -v /path/on/host:/path/in/container:ro \ my_image ``` - Read-Write (`rw`) ```shell # volume: $ docker run -d --name my_container -v my_volume:/data:rw my_image # bind mounts: $ docker run -d \ --name my_container \ -v /path/on/host:/path/in/container:rw \ my_image ``` ---- #### **Docker(Container) Registries** ![image](https://hackmd.io/_uploads/rJ8QRAFNa.png) ---- more registries... ![image](https://hackmd.io/_uploads/Sk-sTRKEa.png =800x) --- ## Installation and Configuration ---- refer to: https://docs.docker.com/engine/install/ ---- ![image](https://hackmd.io/_uploads/r15AM754T.png) ---- ![image](https://hackmd.io/_uploads/rykgmm9E6.png) ---- ### Play with Docker 如果你不想安裝 Docker,有另一種方式可以練習 Docker,那就是 Docker 官方提供的 [Play with Docker (PWD)](https://labs.play-with-docker.com/) ![image](https://hackmd.io/_uploads/Bk-Zmm54T.png =460x) ---- ### Configuring Docker for your environment After installing Docker, when you first run `docker info` to check Docker system information, you might encounter a permission error because the Docker daemon binds to a Unix socket owned by `root`. By default, non-root users need to prepend `sudo` to Docker commands. ---- #### Reason The permission issue arises because the Docker daemon uses a Unix socket, not a TCP port, and by default, it's owned by `root`. To use Docker commands without `sudo`, you need to be part of the `docker` Unix group that has permissions to read/write to the Unix socket. ---- #### Solution To avoid using `sudo` with every Docker command, create a `docker` group and add your user to it. This allows your user to run Docker commands without `sudo`. However, be aware that being part of the `docker` group gives privileges equivalent to the `root` user, which can affect system security. ---- #### Steps to add your user to the `docker` group: ---- 1. Create the `docker` group. ```shell $ sudo groupadd docker ``` 2. Add your user to the `docker` group. ```shell $ sudo usermod -aG docker $USER ``` 3. Log out and log back in, or restart your VM to apply these changes. 4. Verify your user is added to the `docker` group. ```shell $ id -nG ``` For users not logged in, use: ```shell $ sudo usermod -a -G docker <username> ``` ---- ### Docker Desktop ![image](https://hackmd.io/_uploads/B1G4M79N6.png) --- ## Basic Docker Operations see: https://hackmd.io/@titangene/rk3zjKVIz --- ## Docker Compose ![image](https://hackmd.io/_uploads/HytdjXc46.png) ---- Compose is an orchestration tool that makes spinning up ==multi-container== distributed applications with Docker an effortless task. It is easy to define a multi-container application and accompanying services in one file, then spin it up in a ==single command== that will start and run your entire app. ---- ![image](https://hackmd.io/_uploads/HkWf6Q5Np.png) ---- ### history ![image](https://hackmd.io/_uploads/HkafcQ5Va.png) ---- ### Introduction to multi-container applications ![image](https://hackmd.io/_uploads/HkOS27546.png) ---- - Dont want to create each container one by one? - Dont want to configure network, volumes... with cli line by line? ---- ### Writing and understanding docker-compose files ![MJIigm9](https://hackmd.io/_uploads/rJRop794a.jpg =600x) ---- todo list continue... ![image](https://hackmd.io/_uploads/HkOS27546.png) ---- `compose.yaml` ```yaml services: app: image: node:18-alpine command: sh -c "yarn install && yarn run dev" ports: - 127.0.0.1:3000:3000 working_dir: /app volumes: - ./:/app environment: MYSQL_HOST: mysql MYSQL_USER: root MYSQL_PASSWORD: secret MYSQL_DB: todos mysql: image: mysql:8.0 volumes: - todo-mysql-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: todos volumes: todo-mysql-data: ``` ---- ### Running and managing composed applications check this out: https://github.com/docker/awesome-compose my codespace to demo (you cant access): https://github.com/codespaces/curly-spoon-vr6g6jp6qrr2p9vg I will show the /flask-redis example ---- the website at port $8000$ should look like this: ![image](https://hackmd.io/_uploads/BJzP7N9V6.png) and after you reload the page the number would increase ---- ### Strategies for database and stateful application handling ---- #### use docker to compose a primary-replica postgres SQL ![image](https://hackmd.io/_uploads/SJEVN4qE6.png) ---- https://github.com/eremeykin/pg-primary-replica https://github.com/peterxcli/pg-primary-replica ```yaml! version: '3.8' x-postgres-common: &postgres-common image: postgres:14-alpine user: postgres restart: always healthcheck: test: 'pg_isready -U user --dbname=postgres' interval: 10s timeout: 5s retries: 5 services: postgres_primary: <<: *postgres-common ports: - 5432:5432 environment: POSTGRES_USER: user POSTGRES_DB: postgres POSTGRES_PASSWORD: password POSTGRES_HOST_AUTH_METHOD: "scram-sha-256\nhost replication all 0.0.0.0/0 md5" POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256" command: | postgres -c wal_level=replica -c hot_standby=on -c max_wal_senders=10 -c max_replication_slots=10 -c hot_standby_feedback=on volumes: - ./00_init.sql:/docker-entrypoint-initdb.d/00_init.sql - pgdata_primary:/var/lib/postgresql/data postgres_replica: <<: *postgres-common ports: - 5433:5432 environment: PGUSER: replicator PGPASSWORD: replicator_password command: | bash -c " until pg_basebackup --pgdata=/var/lib/postgresql/data -R --slot=replication_slot --host=postgres_primary --port=5432 do echo 'Waiting for primary to connect...' sleep 1s done echo 'Backup done, starting replica...' chmod 0700 /var/lib/postgresql/data postgres " depends_on: - postgres_primary volumes: - pgdata_replica:/var/lib/postgresql/data volumes: pgdata_primary: driver: local pgdata_replica: driver: local ``` ---- test if we run successfully ```shell $ psql postgres://user:password@localhost:5432/postgres -xc \ 'select * from pg_replication_slots;' -[ RECORD 1 ]-------+----------------- slot_name | replication_slot plugin | slot_type | physical datoid | database | temporary | f active | t active_pid | 60 xmin | catalog_xmin | restart_lsn | 0/3000148 confirmed_flush_lsn | wal_status | reserved safe_wal_size | two_phase | f ``` --- ## ~~Advanced~~ More Use Cases ---- ### Github action build image and push to docker hub public container registry ![image](https://hackmd.io/_uploads/rkiwyrcET.png) ---- dockerfile https://github.com/peterxcli/basic-auth-gin/blob/main/Dockerfile ```dockerfile #build stage FROM golang:alpine AS builder RUN apk add --no-cache git WORKDIR /app COPY . /app RUN go build -o /app/main -v #final stage FROM alpine:latest RUN apk --no-cache add ca-certificates COPY --from=builder /app/main /app/main COPY --from=builder /app/public /app/public WORKDIR /app ENTRYPOINT /app/main LABEL Name=mongogo Version=0.0.1 EXPOSE 9000 ``` ---- docker compose file https://github.com/peterxcli/basic-auth-gin/blob/main/.github/workflows/ci.yml ```yaml # This workflow will build a golang project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go name: ci on: [push, pull_request] env: IMAGE_TAG: ${{ github.sha }} jobs: test: runs-on: ubuntu-latest strategy: matrix: mongodb-version: ["6.0"] steps: - uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v3 with: go-version: 1.19 - name: Start MongoDB uses: supercharge/mongodb-github-action@1.8.0 with: mongodb-version: ${{ matrix.mongodb-version }} - name: Build run: go build -v -buildvcs=false ./... - name: Test env: EMAIL: ${{ secrets.EMAIL }} SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }} run: make test build: needs: [test] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up QEMU uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Build and push uses: docker/build-push-action@v4 with: push: false context: . file: ./Dockerfile tags: peter0814/basic-auth-gin:${{ github.sha }} outputs: type=docker, dest=docker.tar - name: docker login env: DOCKER_USER: ${{ secrets.DOCKER_USER }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} run: | docker login -u $DOCKER_USER -p $DOCKER_PASSWORD - name: cache uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1 with: name: docker.tar path: docker.tar push: if: ${{ github.ref == 'refs/heads/release' }} needs : [build] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up QEMU uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: docker login env: DOCKER_USER: ${{ secrets.DOCKER_USER }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} run: | docker login -u $DOCKER_USER -p $DOCKER_PASSWORD - name: Download artifact uses: actions/download-artifact@v2 with: name: docker.tar path: ./ - name: Load Docker image run: | docker load --input docker.tar docker image ls -a - name: Docker Push run: docker push peter0814/basic-auth-gin:$IMAGE_TAG ``` ---- ### Dev Container a truely isolated and consisent development environment https://github.com/peterxcli/basic-auth-gin/tree/main/.devcontainer ---- #### some integrated features in vscode 1. vscode extension as Code (VSCEAC) ![image](https://hackmd.io/_uploads/HyNDgH5V6.png) --- ## reference ### [Docker 系列筆記](https://hackmd.io/@titangene/docker-collection/)
{"title":"Docker 社課","description":"https://chat.openai.com/share/07a56c87-c62a-4506-b708-00421a1801c5","slideOptions":"{\"type\":\"slide\",\"slideOptions\":{\"spotlight\":{\"enabled\":true},\"transition\":\"slide\"}}","contributors":"[{\"id\":\"60f87ada-c8bc-4f5d-9b91-2a0d3103440d\",\"add\":40159,\"del\":17046}]"}
    379 views