Docker搭建Swift项目服务端

场景

最近想写一个App,但是需要依赖几个Api,因为是iOS开发,所以用熟悉的语言开发最为方便,所以直接选择了用Swift语言的Vapor框架开发一个服务端。
可以直接在服务端搭建,也可以使用Docker,这里主要讲使用Docker如何创建Swift项目镜像。

Linux 系统环境

centOS-7

Docker 安装

Docker官网

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 更新包库
sudo yum update

# 卸载老版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

# 安装docker
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装docker
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 启动docker
sudo systemctl start docker

# 设置为开机自启动
sudo systemctl enable docker

# 验证 Docker 是否安装成功
sudo docker --version

Docker 安装 Swift

这里是用 swift 5.7.2 版本,最新版本可以使用 latest 代替。

1.从DockerHub中获取 swift 镜像

1
2
# 拉取Swift
docker pull swift:5.7.2

2.创建一个最新版本的容器

1
docker run --privileged --interactive --tty --name swift-5.7.2 swift:5.7.2 /bin/bash
  • docker run: 在 Docker 中运行一个容器。
  • privileged: 赋予容器运行时更多的权限。
  • interactive (-i): 交互式运行容器。
  • tty (-t): 为容器分配一个伪终端 (TTY)。
  • name swift-5.7.2: 为容器指定名称为 swift-5.7.2。
  • swift:5.7.2: 使用的镜像名称及其版本号。
  • /bin/bash: 在容器中启动 Bash shell。

执行这个命令后,如果一切正常,您应该已经进入了名为 swift-5.7.2 的容器中的 Bash 终端。容器中的 Bash 终端会显示出一些类似 root@container_id:/# 这样的命令行提示符,表示您已经成功地进入了容器。

可以在容器内输入 swift --version,如果输出 swift 版本,表示成功

1
2
3
root@28aed6742a83:/# swift --version
Swift version 5.7.2 (swift-5.7.2-RELEASE)
Target: x86_64-unknown-linux-gnu

3.退出容器

1
exit

4.关闭交互模式(不退出容器进程):

1
2
3
Ctrl + P
#
Ctrl + Q

4.重新进入容器交互模式

1
2
3
docker exec -it swift-5.7.2 /bin/bash
#
docker attach swift-5.7.2

5.停止容器

1
docker stop swift-5.7.2

6.启动容器

1
docker start swift-5.7.2

创建 Swift 项目镜像

通过Vapor创建 Swift 服务项目

在项目根目录创建构建Docker镜像文件Dockerfile(一般Vapor创建项目时候默认创建)

Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# ================================
# Build image
# ================================

# 设置运行环境镜像
FROM swift:5.7.2 as build

# 设置工作路径
WORKDIR /build

# 首先解决依赖关系。
# 这将创建一个可以重用的缓存层
# 只要你的Package.swift/ package .resolve 文件不改变。,将不会重新
COPY ./Package.* ./
RUN swift package resolve

# 项目复制到容器
COPY . .

# 打包
# RUN ulimit -n 65535
RUN swift build -c release --disable-sandbox

# 设置权限
RUN chmod +x ./.build/release/App

# Docker绑定到端口8001
EXPOSE 8001

# 运行项目
# 为了避免问题,本地单独写了一个shell文件来执行启动脚本
ENTRYPOINT ["/bin/bash", "./run.sh"]
# CMD ["/.build/release/App"]

run.sh文件放在项目根目录即可!

1
2
3
# 调用服务启动文件
# 日志输入到docker容器的 /var/log/testservice.log 文件。
/build/.build/release/App > /var/log/testservice.log 2>&1 & tail -f /dev/null

解释:
/build/.build/release/App: 启动名为 App 的应用程序或服务。通常这是一个可执行文件的路径。
> /var/log/testservice.log 2>&1: 将 App 的标准输出(stdout)和标准错误输出(stderr)都重定向到 /var/log/testservice.log 文件中。2>&1 的意思是将标准错误输出重定向到与标准输出相同的位置。
&: 将整个命令放入后台运行,使得应用程序在容器中以后台进程的方式运行。
tail -f /dev/null: 这个命令的作用是让容器保持运行状态,tail -f 用来监视文件的变化,而 /dev/null 是一个空文件,不会有变化。这种做法通常是为了保持容器的运行,让其不会因为前台进程退出而关闭。
总体来说,这个命令的目的是在容器中启动一个应用程序,并将其输出重定向到日志文件,然后以后台进程的方式运行,并且保持容器持续运行。

Apple 官网给出的 Docker 设置 Swift 教程

https://github.com/apple/swift-docker

打包项目镜像

当我们 Swift 服务代码开发完成,现在开始打包镜像:

1
docker build -t testservice:0.0.1 .

testservice 为镜像名称。
0.0.1 为版本号,默认为 lasted。

打包结束后输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[+] Building 483.4s (12/12) FINISHED                             docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 2.81kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 58B 0.0s
=> [internal] load metadata for docker.io/library/swift:5.7.2 0.0s
=> [1/7] FROM docker.io/library/swift:5.7.2 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 18.70kB 0.0s
=> CACHED [2/7] WORKDIR /build 0.0s
=> CACHED [3/7] COPY ./Package.* ./ 0.0s
=> CACHED [4/7] RUN swift package resolve 0.0s
=> [5/7] COPY . . 0.1s
=> [6/7] RUN ulimit -n 65535 0.2s
=> [7/7] RUN swift build -c release 473.2s
=> exporting to image 9.9s
=> => exporting layers 9.8s
=> => writing image sha256:d67221874fd1e557ca196911d48786d45e2c687f78cd9 0.0s
=> => naming to docker.io/library/testservice:0.0.1

打包成功后,可以通过 docker images来查看本地存储的所有镜像。例:

1
2
3
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
testservice 0.0.1 0f938eb2e4c3 2 minutes ago 2.98GB
swift 5.7.2 e96e4b0f7e83 12 months ago 2.15GB

如果需要分享或上传这个镜像到 DockerHub 或其他的镜像仓库中,你可以使用 docker push 命令,不需要忽略即可:

1
docker push docker.io/library/testservice:0.0.1

启动镜像

1
2
3
4
docker run -it --privileged --name testservice -d -p 8001:8001 testservice:0.0.1 /bin/bash

# 需要挂在python环境
# docker run -it --privileged --name testservice -d -p 8001:8001 -v /usr/bin/python3:/usr/local/python3 testservice:0.0.1 /bin/bash

-it: 使用交互模式启动容器,并分配一个伪终端(pseudo-TTY),使得用户能够与容器进行交互。
--name 定义启动的容器名称。
-d: 为后台运行,不需要进入容器。
-p: 为端口映射,将主机的 8001 端口映射到容器的 8001 端口,允许外部访问容器内的服务。
-v: 环境映射,将主机的 /usr/bin/python3 目录挂载到容器的 /usr/local/python3 目录(不需要 python 环境直接忽略)。
testservice:0.0.1: 要运行的镜像名称及版本。
/bin/bash: : 在容器内运行的命令,这会在容器启动时直接进入 Bash Shell。

启动后可以访问 swift 服务地址,访问成功即完成!
启动服务

查看 docker 日志

1
docker logs <container_id>

1
2
3
4
# 先进入到容器查看日志
docker exec -it testservice:0.0.1 /bin/bash
# 监听日志文件变动
taif -f /var/log/testservice.log

介绍几个可能会常用到的命令

停止容器

1
docker stop <容器ID>

启动

1
docker start <容器ID>

查看所有服务(包括停止的增加 -a 参数)

1
docker ps -a

删除服务

1
docker rm <容器ID>

删除镜像

1
docker rmi <容器ID>

Docker 的优势

Vapor官网解释:
使用 Docker 部署 Vapor 应用程序有几个好处:
你的 dockerized 应用程序可以在任何带有 Docker 守护进程的平台上使用相同的命令可靠地启动,即 Linux(CentOS、Debian、Fedora、Ubuntu)、macOS 和 Windows。
你可以使用 docker-compose 或 Kubernetes 清单来编排完整部署所需的多个服务(例如 Redis、Postgres、nginx 等)。
测试应用程序水平扩展的能力很容易,也可以在开发机器上本地测试。
使用 Docker 在本地运行整个服务器堆栈以进行测试对于大型和小型服务器端应用程序都非常有价值。

END