本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2020年02月14日 统计字数: 4838字 阅读时间: 10分钟阅读 本文链接: https://soulteary.com/2020/02/14/scripting-from-the-upgrade-server-system.html ----- # 从升级服务器系统聊聊编写脚本 当服务器运行几十天或几百天后,你总会收到需要报警、许多需求积压:又有严重漏洞了,软件如果不升级那么新的功能使用起来很麻烦... 即使容器技术已经帮助我们减少了许多生产开发过程中对于服务器的依赖,然而运行容器的宿主机总归还是需要一些基础的维护。 如果你的服务器数量不多,那么登陆每一台机器,然后执行命令显然效率并不低,但是如果需要维护的机器从几台变更为几十台后,你就不得不编写一些简单的脚本了。 本文就来聊聊编写简单的升级脚本。 ## 写在前面 除了使用 SCP / RSYNC 进行加密传输外,还可以使用 HTTPS 进行文件传输,就像各种大名鼎鼎的开源软件一样。 启动一个 HTTPS 服务,可以参考之前 [配置基于Traefik v2的 Web 服务器](https://soulteary.com/2020/02/01/configure-traefik-v2-based-web-server.html) 一文中的方案,配合 Nginx 快速启动一个“浏览传输加密的” Web 服务。 ## 如何升级使用 apt 安装的软件包 容器帮助我们解决了不少生产开发环境不一致的问题,其中一个很重要的因素就是软件版本碎片化的问题。 当你有多台服务器的时候,有会遇到这个问题,如果我们需要将 Docker 的版本统一,那么该怎么处理呢? 以 Ubuntu 为例,首先要显式的声明所需软件版本,比如 `19.03.06`,然后判断是否安装过 **Docker**,为安装过软件,则进行指定版本安装,如已经安装过,则将软件升级到指定版本。最后使用 `apt-mark` 将 docker-ce 锁定在当前安装版本,避免系统其他软件滚动升级时,造成破坏。 ```bash #!/bin/bash DOCKER_VERSION=19.03.6 DOCKER_DEB_VERSION=5:19.03.6~3-0~ubuntu-bionic DOCKER_NEED_UPGRADED=0 if DOCKER_BINARY_PATH="$(which docker)"; then echo "Docker Path: $DOCKER_BINARY_PATH"; if $(docker --version | grep -q "$DOCKER_VERSION"); then echo "Docker is ready for use." else echo "Docker needs to be upgraded." DOCKER_NEED_UPGRADED=1 fi if [ "$DOCKER_NEED_UPGRADED" = "1" ]; then apt upgrade docker-ce=$DOCKER_DEB_VERSION -y echo "Docker upgraded to $DOCKER_VERSION." fi else echo "Docker is ready to install." apt install docker-ce=$DOCKER_DEB_VERSION -y fi apt-mark hold docker-ce ``` 如果在国内云服务器上运行,比如阿里云,为了安装过程更快速,我们可以在执行升级安装脚本前先进行软件源的替换。 ```bash if CHANGE_DOCKER_MIRROR="$(cat /etc/apt/sources.list | grep 'download.docker.com')"; then sed -i -e "s/https:\/\/download.docker.com/http:\/\/mirrors.cloud.aliyuncs.com\/docker-ce/" /etc/apt/sources.list echo "Docker-ce mirror set." fi ``` ## 更新三方独立二进制软件 除了从 apt 直接安装的软件包外,我们还会遇到直接从它处下载的二进制软件。脚本大体上可以参考上面,同样先判断软件是否存在,然后再进行下载安装。 以 `compose` 为例,这里因为独立二进制软件不需要考虑“升级”和“首次安装”,所以可以将两个步骤合并在一起,仅需要对初始化的环境变量做一些小的调整即可: ```bash #!/bin/bash COMPOSE_VERSION=1.25.3 COMPOSE_NEED_UPGRADED=1 if COMPOSE_BINARY_PATH="$(which docker-compose)"; then echo "Compose Path: $COMPOSE_BINARY_PATH"; if $(docker-compose --version | grep -q "$COMPOSE_VERSION"); then echo "Compose is ready for use." COMPOSE_NEED_UPGRADED=0 else echo "Compose needs to be upgraded." fi fi if [ "$COMPOSE_NEED_UPGRADED" = "1" ]; then curl -L -k https://cdn.lab.com/docker-compose -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose echo "Compose upgraded to $DOCKER_VERSION." docker-compose --version fi ``` 这里为了减少升级在下载软件方面浪费的时间,可以将 **docker-compose** 下载并放置于你的服务器获取数据更快的位置。 ## 更新容器镜像 在升级容器服务之前,我们一般会预拉取容器镜像,国内服务器从 Docker Hub 获取数据始终不够快,所以这里可以采取两个方案。 ### 私有仓库 如果团队有私有仓库,愿意在项目中对**未做更改的镜像**使用私有名称而非官方名称,或者愿意将官方的仓库打私有标签存入仓库的话。 以 traefik 为例,将下载到本地的官方镜像打上私有仓库,然后推送私有仓库储存: ```bash docker tag traefik:v2.1.3 docker.lab.com/traefik:v2.1.3 docker push docker.lab.com/traefik:v2.1.3 ``` 之后使用的话,软件直接执行 `docker pull` 就好了。 ### 作为压缩包数据导入 如果不想额外维护镜像仓库,或者不希望将未做任何更改的官方镜像改名使用,可以将官方镜像导出为压缩包,然后通过目标机器下载再导入的方式更新指定版本的容器镜像。 导出很简单,一条命令即可将你在本地/服务器下载好的软件包单独保存出来: ```bash docker save traefik:v2.1.3 -o traefik-v2.1.3.tar ``` 同样需要先将软件包放置于服务器获取更快的 Web 服务器上,然后使用下面的脚本就可以进行容器软件升级了。 ```bash #!/bin/bash TRAEFIK_VERSION=2.1.3 if [ "$(docker images -q traefik:v$TRAEFIK_VERSION)" = "" ]; then curl -L -k https://cdn.lab.com/traefik-v$TRAEFIK_VERSION.tar -o /tmp/traefik-v$TRAEFIK_VERSION.tar docker load -i /tmp/traefik-v$TRAEFIK_VERSION.tar rm /tmp/traefik-v$TRAEFIK_VERSION.tar fi echo "Traefik is ready for use." ``` ## 升级系统其他软件 常见系统漏洞一般使用下面的万金油命令就能解决了。 ```bash apt update && apt upgrade -y ``` 如果你讨厌每次升级后遗留的过时无用的软件包,可以使用 `autoremove` 参数移除掉不用的软件。 ```bash apt update && apt upgrade -y && apt autoremove -y ``` 当然,最重要的一点,如果升级的软件包,包含 `kernel-` 前缀,需要重启服务器,才能让变更生效哦。 ## 完整的示例 将上面的脚本片段组合在一起,将得到一个接近真实场景使用的升级脚本(示例未使用云服务商软件源): ```bash #!/bin/bash DOCKER_VERSION=19.03.6 DOCKER_DEB_VERSION=5:19.03.6~3-0~ubuntu-bionic DOCKER_NEED_UPGRADED=0 if DOCKER_BINARY_PATH="$(which docker)"; then echo "Docker Path: $DOCKER_BINARY_PATH"; if $(docker --version | grep -q "$DOCKER_VERSION"); then echo "Docker is ready for use." else echo "Docker needs to be upgraded." DOCKER_NEED_UPGRADED=1 fi if [ "$DOCKER_NEED_UPGRADED" = "1" ]; then apt upgrade docker-ce=$DOCKER_DEB_VERSION -y echo "Docker upgraded to $DOCKER_VERSION." fi else echo "Docker is ready to install." apt install docker-ce=$DOCKER_DEB_VERSION -y fi apt-mark hold docker-ce COMPOSE_VERSION=1.25.3 COMPOSE_NEED_UPGRADED=1 if COMPOSE_BINARY_PATH="$(which docker-compose)"; then echo "Compose Path: $COMPOSE_BINARY_PATH"; if $(docker-compose --version | grep -q "$COMPOSE_VERSION"); then echo "Compose is ready for use." COMPOSE_NEED_UPGRADED=0 else echo "Compose needs to be upgraded." fi fi if [ "$COMPOSE_NEED_UPGRADED" = "1" ]; then curl -L https://cdn.lab.com/docker-compose -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose echo "Compose upgraded to $DOCKER_VERSION." docker-compose --version fi TRAEFIK_VERSION=2.1.3 if [ "$(docker images -q traefik:v$TRAEFIK_VERSION)" = "" ]; then curl -L https://cdn.lab.com/traefik-v$TRAEFIK_VERSION.tar -o /tmp/traefik-v$TRAEFIK_VERSION.tar docker load -i /tmp/traefik-v$TRAEFIK_VERSION.tar rm /tmp/traefik-v$TRAEFIK_VERSION.tar fi echo "Traefik is ready for use." apt update && apt upgrade -y && apt autoremove -y ``` 如果你将脚本保存为 `upgrade.sh` 然后也放到一个你的服务器访问比较快的地方,那么你可以这样使用它: ```bash curl -L https://cdn.lab.com/upgrade.sh | bash ``` ## 最后 对于工程师来说,正确的偷懒是美德。 --EOF