跳到正文Lab 03: Containerisation and Docker · Jimmy Huang's BlogLab 03: Containerisation and Docker
本页目录· 65 条
这一页有部分内容暂时无法显示,其余内容完整。
Lab学后总结›Overview
容器化是将应用程序及其所有相关的配置文件、库和运行时依赖捆绑在一起的过程。与传统的应用构建和部署方法相比,容器化简化了构建应用并部署到任何支持平台上的过程,因为由于配置错误、库缺失等风险和漏洞被最大限度地减少。
容器常被拿来与虚拟机比较,因为它们都允许在一个封闭的环境中运行多种类型的软件。容器是应用层的抽象,而虚拟机则提供硬件抽象层。虽然每个容器单独运行进程,但多个容器共享底层操作系统;然而,在虚拟机中,任何访客操作系统都位于主机操作系统和虚拟机管理程序之上。
Goal
本实验室旨在教授你使用容器的基本但基础概念,并涵盖使用 Docker 前你需要掌握的关键命令,Docker 是最著名且广泛使用的容器技术之一。你还将学习如何将典型应用容器化,将其转换为图片,并可选择标记并上传到本地/远程仓库。
Task 0: Create a Cloud VM
使用Nectar创建一个2 vCPUS 4GB RAM的Ubuntu 24.04 LTS
可参考
Lab 01: Nectar, OpenStack and Parallel Computing & Programming
Flavor 改成 m3.small
Task 1: 安装 Docker
先装好工具
告诉系统“Docker 的软件包从哪里下载”
验证下载源可信(GPG key)
再正式安装 Docker
依次执行命令
第 1 组:安装基础依赖
第 2 组:创建 keyrings 目录
第 3 组:下载 Docker 官方 GPG key
第 4 组:调整 key 文件权限
第 5 组:添加 Docker 官方软件源
第 6 组:更新软件包索引
第 7 组:安装 Docker
安装完后做权限配置
Task 2: 运行 Hello World 容器
跑第一个容器
看容器列表
看镜像列表
拉一个 ubuntu 镜像
在 ubuntu 容器里执行命令
理解“容器里不止能跑一个命令”
Step 2.1: 运行 hello-world
Step 2.2: 查看所有容器
Step 2.3: 查看本机镜像
Step 2.4: 拉取 ubuntu 镜像
Step 2.5: 在 ubuntu 容器中执行一条命令
Step 2.6: 在 ubuntu 容器中执行多条命令
Task 3: 管理容器
交互式进入容器
退出容器
后台运行容器
进入已经运行的容器
停止、删除容器
Step 3.1 交互式进入一个 Ubuntu 容器
进入容器内部,提示符会变,像是在另一台小 Linux 机器里
Step 3.2: 再看容器列表
Step 3.3: 用 --rm 运行一个自动删除的容器
这是运行了一个自动删除的ubuntu容器,退出后不会留在列表中
Step 3.4: 后台运行一个容器
Step 3.5: 进入一个已经在运行的容器
Step 3.6: 停止容器
Step 3.7: 删除容器
Step 3.8: 删除所有容器
docker ps -a -q:列出所有容器 ID
docker rm -f ...:强制删除这些容器
Task 4: 在容器里运行 Web Server
启动一个 nginx 容器
把宿主机的 8080 映射到容器里的 80
把宿主机目录挂载到 nginx 网页目录
用 curl 和浏览器访问网页
Step 4.1:先启动一个最简单的 nginx 容器
Step 4.2:先在服务器本机测试 nginx 是否通了
Step 4.3:创建宿主机目录,准备挂载网页文件
文档要求你创建本地目录 /home/ubuntu/www,并把它挂载到 nginx 容器中的网页目录。
为什么要做这一步
因为你要把宿主机上的网页文件放到这个目录里,然后通过卷挂载让 nginx 去读它。
Step 4.4:删除刚才的 nginx 容器,重新用挂载方式启动
Step 4.5:创建你自己的 index.html
Step 4.6:再次用 curl 测试网页
Step 4.7:开放 8080 端口,再用浏览器访问
在Nectar → Network → Security Groups里开放8080端口
Step 4.8:浏览器访问
Task 5: 创建 Docker Images
创建一个新目录存放项目文件
写一个简单的 FastAPI 程序
写一个 Dockerfile
构建镜像
用这个镜像启动容器并测试接口
Step 5.1: 先创建项目目录
Step 5.2: 创建 requirements.txt
告诉 Docker:这个 Python 应用运行时需要安装 fastapi[standard] 这个依赖。
Step 5.3: 创建 my_script.py
Step 5.4: 创建 Dockerfile
Step 5.5: 理解这个 Dockerfile 每一行
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
CMD ["python","-m","fastapi","run","/code/my_script.py"]
容器启动后默认执行这条命令
也就是启动 FastAPI 服务
为什么这里要这样写
因为一个能运行的 Docker 镜像,至少要包含三样东西:
Dockerfile 本质上就是把这三件事写清楚。
Step 5.6: 构建镜像
Step 5.7: 查看镜像是否构建成功
Step 5.8: 用这个镜像启动容器
Step 5.9: 在服务器本机测试
Step 5.10 开放8000端口,外部访问
Task 6: Docker Compose
Step 6.1:先清理占用 8000 的旧容器
Step 6.2:创建新目录
Step 6.3:创建 requirements.txt
Step 6.4:创建 app.py
这个程序每次访问 / 时,都会去 Redis 里把 hits 这个计数器加 1,然后返回累计次数。
Step 6.5:创建 Dockerfile
Step 6.6:创建 docker-compose.yml
Step 6.7:启动 Compose
Step 6.8:查看服务状态
Step 6.9:测试 Web 服务
Step 6.10:停止和清理服务
Summary
这周的主题是:Containerisation and Docker(容器化与 Docker)。
这周不是单纯学命令,而是在学一个非常重要的现代开发/部署思路:
把程序和它依赖的环境一起打包,让它能更稳定地在别的机器上运行。
先创建一台云主机
在上面安装 Docker
跑最简单的测试容器
学会管理容器
用 nginx 跑一个网页服务
把自己的 FastAPI 应用做成镜像
最后用 Docker Compose 同时启动 Web 和 Redis 两个服务
Dockerfile vs docker-compose.yml
如果一个服务可以直接使用现成镜像,比如 Redis、MySQL、Nginx,那在 docker-compose.yml 里直接写image:
如果一个服务是我自己写的,或者我要在现成基础上加自己的代码、依赖、配置,那就需要 Dockerfile 来定义这个镜像怎么构建。
docker-compose.yml 可以同时管理多个服务,其中有的服务用image:,有的服务用build:,两种都可以混着用。
build x dockerfile x context
sudo apt install -y apt-transport-https ca-certificates curl gnupg software-properties-common lsb-release
http://<ip>:8000/items/5?q=test
{"item_id":5, "q":"test"}
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker ${USER}
docker run ubuntu echo "Hello from ubuntu!"
docker run ubuntu sh -c "echo Hello FIT5225 from ubuntu; ls; echo bye guys"
docker run -t -d ubuntu
docker ps
docker exec -it ubuntu bash
pwd
ls
echo "I am inside a running container"
exit
docker stop funny_lehmann
docker ps -a
docker rm myubuntu
docker ps -a
docker rm -f $(docker ps -a -q)
docker run --name myserver -p 8080:80 -d nginx
curl http://localhost:8080
sudo mkdir -p /home/ubuntu/www
sudo chown ubuntu:ubuntu -R /home/ubuntu/www
docker run --name myserver -p 8080:80 -d -v /home/ubuntu/www:/usr/share/nginx/html nginx
cat > /home/ubuntu/www/index.html <<'EOF'
<html>
<head>
<title>Docker Lab</title>
</head>
<body>Nginx server is running!</body>
</html>
EOF
cat /home/ubuntu/www/index.html
curl http://localhost:8080
http://Your Ip Address:8080
mkdir demo_jimmy
cd demo_jimmy
cat > requirements.txt <<'EOF'
fastapi[standard]
EOF
cat > my_script.py <<'EOF'
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def hello():
return "Welcome to FIT5225!"
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
EOF
cat > Dockerfile <<'EOF'
FROM python:3.12-alpine
WORKDIR /code
COPY my_script.py /code
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
CMD ["python","-m","fastapi","run","/code/my_script.py"]
EOF
docker build -t fastapi-app .
docker run -d -p 8000:8000 --name my-fastapi-app fastapi-app
curl http://localhost:8000
docker rm -f my-fastapi-app
mkdir compose-demo
cd compose-demo
cat > requirements.txt <<'EOF'
fastapi[standard]
redis
EOF
cat > app.py <<'EOF'
import time
import redis
from fastapi import FastAPI
app = FastAPI()
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.get('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
EOF
cat > Dockerfile <<'EOF'
FROM python:3.12-alpine
WORKDIR /code
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["python","-m","fastapi","run","app.py"]
EOF
cat > docker-compose.yml <<'EOF'
services:
web:
build: .
ports:
- "8000:8000"
redis:
image: "redis:alpine"
EOF
curl http://localhost:8000
project/
├── docker-compose.yml
├── web/
│ ├── Dockerfile
│ ├── app.py
│ └── requirements.txt
├── worker/
│ ├── Dockerfile
│ └── worker.py
└── frontend/
├── Dockerfile
└── package.json
services:
web:
build: ./web
worker:
build: ./worker
frontend:
build: ./frontend
redis:
image: redis:alpine
services:
web:
build:
context: ./web
dockerfile: Dockerfile.prod
build
├── context -> 去哪个目录找构建材料
└── dockerfile -> 在那个目录里,用哪个 Dockerfile