2017年4月9日星期日

OpenShift_074:亲和性和反亲和性测试

环境 OCP 3.4

测试场景 1. 各个 Node 节点没有设置特别的 label

1.1 确认 Node 节点配置没有设置特别的 label
oc get node --show-labels
输出如下:
NAME                 STATUS                     AGE       LABELS
master.example.com   Ready,SchedulingDisabled   78d       beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=master.example.com
node1.example.com    Ready                      78d       beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node1.example.com
node2.example.com    Ready                      78d       beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=node2.example.com

1.2 部署 myphp 应用,Replicas=2
多试验几次,发现 2 个 pod 有时会运行在同一个 node 上,有时会运行在 2 个 node 上。
oc get pod -o wide | grep myphp

1.3 手工把 pod 数量降为 0,再升到 1,再升到 2
oc scale --replicas=0 dc/myphp
oc scale --replicas=1 dc/myphp
oc scale --replicas=2 dc/myphp
多试验几次,发现 2 个 pod 有时会运行在同一个 node 上,有时会运行在 2 个 node 上。
这说明,默认情况下 pod 选择运行的 node 是随机的。

1.4 清理应用
oc delete is/myphp bc/myphp dc/myphp svc/myphp routes/myphp

测试场景 2. 对 Node 节点设置特别的 label:region

2.1 对 Node 节点设置 label:region
oc label node node1.example.com region=r1 --overwrite
oc label node node2.example.com region=r2 --overwrite

2.2 部署 myphp 应用,Replicas=2
多试验几次,发现 2 个 pod 只会运行在 region label 值相同的 node  值 上。
这说明 region 默认是亲和性的,一旦第 1 个 pod 运行在某一个 node(region=r1) 上,scale up 的 pod 也会运行在该 node(region=r1)  上。

2.3 手工把 pod 数量降为 0,再升到 1,再升到 2
多试验几次,发现 2 个 pod 只会运行在 region label 值相同的 node  值 上。

2.4 清理应用

测试场景 3. 对 Node 节点设置特别的 label:zone

3.1 对 Node 节点设置 label:zone
oc label node node1.example.com region-
oc label node node2.example.com region-
oc label node node1.example.com zone=z1 --overwrite
oc label node node2.example.com zone=z2 --overwrite

3.2 部署 myphp 应用,Replicas=2
多试验几次,发现 2 个 pod 会运行在 zone label 值不同的 node 上。
这说明 zone 默认是反亲和性的,多个 pod 会运行在 zone label 值不同的 node 上。
// TODO 实际测试结果不是这样,需要再分析原因。

3.3 手工把 pod 数量降为 0,再升到 1,再升到 2
多试验几次,发现 2 个 pod 会运行在 zone label 值不同的 node 上。
// TODO 实际测试结果不是这样,需要再分析原因。

3.4 清理应用

2017年4月7日星期五

OpenShift_073:部署 Spring Boot 应用(Template 方式)

环境:OCP 3.4

1. 复制 Spring Boot builder 镜像 (在 MAC 机器上操作)
docker save -o spring-boot-rhel7.tar.gz maping930883/spring-boot-rhel7:latest
scp spring-boot-rhel7.tar.gz root@192.168.56.112:/opt/ose/images/

2. 修改 Tag,并 Push 到本地镜像仓库 (在 Registry 机器上操作)
cd /opt/ose/images
docker load -i spring-boot-rhel7.tar.gz
docker tag maping930883/spring-boot-rhel7:latest registry.example.com:5000/maping930883/spring-boot-rhel7:latest
docker push registry.example.com:5000/maping930883/spring-boot-rhel7:latest

3. 克隆 springboot-sample-app 到本地 (在 MAC 机器上操作)
cd ~/mygit
git clone https://github.com/codecentric/springboot-sample-app.git
scp -r springboot-sample-app/ root@192.168.56.112:/opt/

4. 初始化 springboot-sample-app git 仓库(在 Registry 机器上操作)
mkdir -p /opt/git/repo/springboot-sample-app.git;
cd /opt/git/repo/springboot-sample-app.git;
git init --bare;
git update-server-info;
mv hooks/post-update.sample hooks/post-update;

5. 拷贝 springboot-sample-app 代码,并提交(在 Registry 机器上操作)
cd /opt
mv springboot-sample-app springboot-sample-app-demo
git clone file:///opt/git/repo/springboot-sample-app.git/

cp springboot-sample-app-demo/* springboot-sample-app -rf;
cp springboot-sample-app-demo/.sti springboot-sample-app -rf;
cp springboot-sample-app-demo/.htaccess springboot-sample-app -rf;
cp springboot-sample-app-demo/.gitignore springboot-sample-app -rf;
cd springboot-sample-app;
git add .;
git commit -m 'initial upload';
git push origin master;

6. 验证 springboot-sample-app git 仓库是否创建成功(在 Master 机器上操作)
cd /tmp
git clone http://git.example.com/git/springboot-sample-app.git/

7. 首次部署 springboot-sample-app 应用 (在 Master 机器上操作)
docker pull registry.example.com:5000/maping930883/spring-boot-rhel7
oc new-app registry.example.com:5000/maping930883/spring-boot-rhel7~http://git.example.com/git/springboot-sample-app.git
本来想先生成应用,然后根据自动创建 IS 和其它资源,导出 template 大致的样子再修改,
但是报告如下错误:
Cloning "http://git.example.com/git/springboot-sample-app.git" ...
error: build error: fatal: dumb http transport does not support --depth
估计是因为我的 git 仓库不是真正的 git server。

只好手工创建 template 啦。

8. 创建 Image Stream (在 Master 机器上操作)
oc create -f spring-boot-rhel7-is.json -n openshift
其中 spring-boot-rhel7-is.json 内容如下:
{
    "kind": "ImageStream",
    "apiVersion": "v1",
    "metadata": {
        "name": "spring-boot-rhel7",
        "creationTimestamp": null
    },
    "spec": {
        "dockerImageRepository": "registry.example.com:5000/maping930883/spring-boot-rhel7",
        "tags": [
            {
                "name": "latest",
                "annotations": null,
                "from": {
                    "kind": "DockerImage",
                    "name": "registry.example.com:5000/maping930883/spring-boot-rhel7"
                },
                "generation": 1,
                "importPolicy": {
                    "insecure": true
                }
            }
        ]
    }
}

确认 TAGS 已经打上,执行 oc get is spring-boot-rhel7 -n openshift
输出如下:
NAME                DOCKER REPO                                                TAGS      UPDATED
spring-boot-rhel7   registry.example.com:5000/maping930883/spring-boot-rhel7   latest    2 seconds ago

9. 创建 Template
oc create -f spring-boot-rhel7-s2i-template.json -n openshift
其中 spring-boot-rhel7-s2i-template.json 内容如下:
{
    "kind": "Template",
    "apiVersion": "v1",
    "metadata": {
        "name": "spring-boot-rhel7-s2i",
        "creationTimestamp": null,
        "annotations": {
            "iconClass": "icon-java",
            "description": "Application template for Spring Boot Web applications built using S2I.",
            "tags": "java,spring boot,rhel7,s2i",
            "version": "1.0"
        }
    },
    "objects": [
        {
            "apiVersion": "v1",
            "kind": "Service",
            "metadata": {
                "annotations": {
                    "description": "The web server's http port."
                },
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}"
            },
            "spec": {
                "ports": [
                    {
                        "port": 8080,
                        "targetPort": 8080
                    }
                ],
                "selector": {
                    "deploymentConfig": "${APPLICATION_NAME}"
                }
            }
        },
        {
            "apiVersion": "v1",
            "id": "${APPLICATION_NAME}-http",
            "kind": "Route",
            "metadata": {
                "annotations": {
                    "description": "Route for application's http service."
                },
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}"
            },
            "spec": {
                "host": "${HOSTNAME_HTTP}",
                "to": {
                    "name": "${APPLICATION_NAME}"
                }
            }
        },
        {
            "apiVersion": "v1",
            "kind": "ImageStream",
            "metadata": {
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}"
            }
        },
        {
            "apiVersion": "v1",
            "kind": "BuildConfig",
            "metadata": {
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}"
            },
            "spec": {
                "output": {
                    "to": {
                        "kind": "ImageStreamTag",
                        "name": "${APPLICATION_NAME}:latest"
                    }
                },
                "source": {
                    "contextDir": "${CONTEXT_DIR}",
                    "git": {
                        "ref": "${SOURCE_REPOSITORY_REF}",
                        "uri": "${SOURCE_REPOSITORY_URL}"
                    },
                    "type": "Git"
                },
                "strategy": {
                    "sourceStrategy": {
                        "forcePull": true,
                        "from": {
                            "kind": "ImageStreamTag",
                            "name": "spring-boot-rhel7:latest",
                            "namespace": "${IMAGE_STREAM_NAMESPACE}"
                        },
                        "env": [
                           {
                               "name": "MAVEN_MIRROR_URL",
                               "value": "${MAVEN_MIRROR_URL}"
                           }
                       ]
                    },
                    "type": "Source"
                },
                "triggers": [
                    {
                        "github": {
                            "secret": "${GITHUB_WEBHOOK_SECRET}"
                        },
                        "type": "GitHub"
                    },
                    {
                        "generic": {
                            "secret": "${GENERIC_WEBHOOK_SECRET}"
                        },
                        "type": "Generic"
                    },
                    {
                        "imageChange": {},
                        "type": "ImageChange"
                    },
                    {
                        "type": "ConfigChange"
                    }
                ]
            }
        },
        {
            "apiVersion": "v1",
            "kind": "DeploymentConfig",
            "metadata": {
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}"
            },
            "spec": {
                "replicas": 1,
                "selector": {
                    "deploymentConfig": "${APPLICATION_NAME}"
                },
                "strategy": {
                    "type": "Recreate"
                },
                "template": {
                    "metadata": {
                        "labels": {
                            "application": "${APPLICATION_NAME}",
                            "deploymentConfig": "${APPLICATION_NAME}"
                        },
                        "name": "${APPLICATION_NAME}"
                    },
                    "spec": {
                        "containers": [
                            {
                                "env": [
                                    {
                                       "name": "MAVEN_MIRROR_URL",
                                       "value": "${MAVEN_MIRROR_URL}"
                                    }
                                ],
                                "image": "${APPLICATION_NAME}",
                                "imagePullPolicy": "Always",
                                "name": "${APPLICATION_NAME}",
                                "ports": [
                                    {
                                        "containerPort": 8778,
                                        "name": "jolokia",
                                        "protocol": "TCP"
                                    },
                                    {
                                        "containerPort": 8080,
                                        "name": "http",
                                        "protocol": "TCP"
                                    }
                                ]
                            }
                        ],
                        "terminationGracePeriodSeconds": 60
                    }
                },
                "triggers": [
                    {
                        "imageChangeParams": {
                            "automatic": true,
                            "containerNames": [
                                "${APPLICATION_NAME}"
                            ],
                            "from": {
                                "kind": "ImageStreamTag",
                                "name": "${APPLICATION_NAME}:latest"
                            }
                        },
                        "type": "ImageChange"
                    },
                    {
                        "type": "ConfigChange"
                    }
                ]
            }
        }
    ],
    "parameters": [
        {
            "name": "APPLICATION_NAME",
            "description": "The name for the application.",
            "value": "springboot-sample-app",
            "required": true
        },
        {
            "name": "HOSTNAME_HTTP",
            "description": "Custom hostname for http service route.  Leave blank for default hostname, e.g.: \u003capplication-name\u003e-\u003cproject\u003e.\u003cdefault-domain-suffix\u003e"
        },
        {
            "name": "SOURCE_REPOSITORY_URL",
            "description": "Git source URI for application",
            "value": "http://git.example.com/git/springboot-sample-app.git",
            "required": true
        },
        {
            "name": "SOURCE_REPOSITORY_REF",
            "description": "Git branch/tag reference",
            "value": "master"
        },
        {
            "name": "CONTEXT_DIR",
            "description": "Path within Git project to build; empty for root project directory.",
            "value": "/"
        },
        {
            "name": "GITHUB_WEBHOOK_SECRET",
            "description": "GitHub trigger secret",
            "generate": "expression",
            "from": "[a-zA-Z0-9]{8}",
            "required": true
        },
        {
            "name": "GENERIC_WEBHOOK_SECRET",
            "description": "Generic build trigger secret",
            "generate": "expression",
            "from": "[a-zA-Z0-9]{8}",
            "required": true
        },
        {
            "name": "IMAGE_STREAM_NAMESPACE",
            "description": "Namespace in which the ImageStreams for Red Hat Middleware images are installed. These ImageStreams are normally installed in the openshift namespace. You should only need to modify this if you've installed the ImageStreams in a different namespace/project.",
            "value": "openshift",
            "required": true
        },
        {
            "name": "MAVEN_MIRROR_URL",
            "description": "maven mirror url",
            "value": "http://192.168.56.1:8081/nexus/content/groups/public/",
            "required": true
        }
    ],
    "labels": {
        "template": "spring-boot-rhel7-s2i"
    }
}

10. 选择 spring-boot-rhel7-s2i 模板,发布应用
经过前面的“艰苦努力”,现在发布 Spring Boot 应用变得超级简单!

11. 清理并重做
如果有错,执行以下命令清理,然后重新执行上述步骤
oc delete bc/springboot-sample-app is/springboot-sample-app dc/springboot-sample-app routes/springboot-sample-app svc/springboot-sample-app
oc delete is spring-boot-rhel7 -n openshift
oc delete template spring-boot-rhel7-s2i -n openshift
docker rmi registry.example.com:5000/maping930883/spring-boot-rhel7:latest

OpenShift_072:构建 Spring Boot Builder Image(基于 rhel7)

环境:MAC OS X 10.12.3 + Docker 1.13.1 + S2I 1.1.5

本文和《构建 Spring Boot Builder Image(基于 centos7)》步骤差不多。
不一样的是 Base Image 使用的是红帽官方的 registry.access.redhat.com/rhscl/s2i-base-rhel7。
好处是镜像来源安全可靠,并且红帽会一直更新维护该镜像,用着放心。

以下操作均在 MAC 机器上执行。

1. 创建 S2I 目录结构
s2i create spring-boot-rhel7 s2i-rhel7-spring-boot
说明:这里 spring-boot-rhel7 是 builder image 名称,s2i-rhel7-spring-boot 是目录名称。

2. 下载并解压 jdk 1.8 和 maven 3
tar zxvf jdk-8u121-linux-x64.tar.gz
mv jdk1.8.0_121 jdk
tar zxvf apache-maven-3.3.9-bin.tar.gz
mv apache-maven-3.3.9 maven

3. 为了能够安装软件,创建 OCP.repo,指向本地 yum 源
OCP.repo 内容如下:
[OpenShift]
baseurl = http://192.168.56.112/repo/
gpgcheck = 0
enabled = 1

4. 定义 Docker file
内容如下:
FROM registry.access.redhat.com/rhscl/s2i-base-rhel7

EXPOSE 8080

LABEL io.k8s.description="Platform for building and running Spring Boot applications" \
      io.k8s.display-name="Spring Boot Maven 3" \
      io.openshift.expose-services="8080:http" \
      io.openshift.tags="builder,java,java8,maven,maven3,springboot"

COPY OCP.repo /etc/yum.repos.d/OCP.repo
RUN yum update -y && \
  yum install -y curl wget git iproute iputils net-tools bash-completion && \
  yum clean all

# 切换镜像的目录,进入 /usr 目录
WORKDIR /usr

# 在 /usr 下创建 java 目录,用来存放 jdk
RUN mkdir java

# 切换镜像的目录至 /usr/java
WORKDIR /usr/java

# 在 /usr/java 下创建 jdk 目录,用来存放 jdk 文件
RUN mkdir jdk

# 切换镜像的目录至 /usr/java/jdk
WORKDIR /usr/java/jdk

# 将宿主机的 jdk 目录下的全部文件考入至镜像的 /usr/java/jdk 目录下
ADD jdk /usr/java/jdk

# 切换镜像的目录至 /usr/share
WORKDIR /usr/share

# 在 /usr/share 下创建 maven 目录,用来存放 maven 文件
RUN mkdir maven

# 将宿主机的 maven 目录下的全部文件考入至镜像的 /opt/face 目录下
ADD maven /usr/share/maven

RUN ln -s /usr/share/maven/bin/mvn /usr/bin/mvn

# 设置时区,容器时间和宿主机时间同步
# RUN rm -f /etc/localtime && ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

# 切换镜像的目录至 /opt/app-root/src
WORKDIR ${HOME}

# 设置环境变量
ENV JAVA_VERSON=1.8.0_121
ENV MAVEN_VERSION=3.3.9
ENV JAVA_HOME=/usr/java/jdk
ENV JAVA_BIN=/usr/java/jdk/bin
ENV PATH=$PATH:$JAVA_HOME/bin
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV MAVEN_HOME=/usr/share/maven

# Add configuration files, bashrc and other tweaks
COPY ./s2i/bin/ $STI_SCRIPTS_PATH

RUN chown -R 1001:1001 /opt/app-root
USER 1001

# Set the default CMD to print the usage of the language image
CMD $STI_SCRIPTS_PATH/usage

5. 定义 s2i/bin/assemble
#!/bin/bash

set -e

echo "---> Installing application source"
cp -Rf /tmp/src/. ./

echo "---> Building Spring Boot application from source"
echo "--> # MVN_ARGS = $MVN_ARGS"
if [ -f "mvnw" ]; then
  ./mvnw clean install $MVN_ARGS
else
  mvn clean install $MVN_ARGS
fi

# Fix source directory permissions
fix-permissions ./

6. 定义 s2i/bin/run
#!/bin/bash

set -e

APP_TARGET=${APP_TARGET:-target}
echo "---> Starting Spring Boot application"
echo "--> # APP_TARGET = $APP_TARGET"
echo "--> # JAVA_OPTS = $JAVA_OPTS"
echo "---> Running application from jar ($(find $APP_TARGET -name *.jar)) ..."
java $JAVA_OPTS -jar `find $APP_TARGET -name *.jar`

7. 定义 s2i/bin/usage
#!/bin/sh -e

cat <This is a STI springboot maven3 centos base image:
To use it, install STI: https://github.com/openshift/source-to-image
Sample invocation:
sti build git://github.com/codecentric/springboot-sample-app spring-boot-centos7 springboot-sample-app
You can then run the resulting image via:
docker run -p 8080:8080 springboot-sample-app
EOF

8. 构建 spring boot builder image
docker build -t="maping930883/spring-boot-rhel7" .

构建后,启动镜像,检查软件是否都安装了
docker run -it maping930883/spring-boot-rhel7 bash

9. 使用 spring boot builder image 构建应用镜像
s2i build git://github.com/codecentric/springboot-sample-app maping930883/spring-boot-rhel7 springboot-sample-app

这里有一个小问题,就是每次构建应用镜像,都会从网上下载 maven 构件,非常耽误时间。
能不能指向自己私有的 maven 仓库呢?
想一想 maven 的知识,嗯,修改 /usr/share/maven/conf/setting.xml 文件内容。
把本机的 ~/.m2/setting.xml 内容复制过去,
修改这一行,指向镜像中自己的 repository
<localRepository>/opt/app-root/src/.m2/repository</localRepository>
还有别忘了把 localhost 改成 IP 地址,如果有的话。

重新构建 builder image,重新构建应用镜像,这次将会下载到自己私有的 maven 仓库,下次构建应用镜像不用重新下载 maven 构件,速度快了很多!

10. 启动应用镜像
docker run -p 8080:8080 springboot-sample-app

访问 http://localhost:8080,显示画面

至此,说明 spring boot builder image 构建成功!

参考文献:
1. https://blog.codecentric.de/en/2016/03/deploy-spring-boot-applications-openshift/
2. https://hub.docker.com/r/codecentric/springboot-maven3-centos/
3. https://github.com/codecentric/springboot-maven3-centos
4. https://blog.openshift.com/using-spring-boot-on-openshift/
5. https://blog.openshift.com/using-openshift-enterprise-grade-spring-boot-deployments/

2017年4月1日星期六

OpenShift_071:从 3.4.0.39 升级到 3.4.1.10

环境:OCP 3.4

1. 删除旧的 repo,在 Registry 机器上做)
cd /opt/ose
rm -rf rhel-7-server-extras-rpms rhel-7-server-ose-3.4-rpms rhel-7-server-rpms

2. 上传新的 repo(在 MAC 机器上做)
scp -r rhel-7-server-extras-rpms root@192.168.56.112:/opt/ose/
scp -r rhel-7-server-ose-3.4-rpms root@192.168.56.112:/opt/ose/
scp -r rhel-7-server-rpms root@192.168.56.112:/opt/ose/

3. 删除旧的 repodata,重新生成新的 repodata(在 Registry 机器上做)
cd /opt/ose
rm -rf repodata

重新生成 repodata
createrepo --worker=5 /opt/ose

4. 升级 docker 以及安装程序(在所有机器上执行)
清除 yum 缓存(在所有机器上执行)
yum clean all

确认最新的 yum 安装包(在所有机器上执行)

yum list | grep -i docker
输出包含如下内容:
...
docker-latest.x86_64                    1.12.6-11.el7              OpenShift  
docker-latest-logrotate.x86_64          1.12.6-11.el7              OpenShift  
docker-latest-v1.10-migrator.x86_64     1.12.6-11.el7              OpenShift
...

yum list | grep -i atomic-openshift
输出包含如下内容:
...
atomic-openshift-docker-excluder.noarch 3.4.1.10-1.git.0.c96aed3.el7
atomic-openshift-dockerregistry.x86_64  3.4.1.10-1.git.0.c96aed3.el7
atomic-openshift-excluder.noarch        3.4.1.10-1.git.0.c96aed3.el7
atomic-openshift-master.x86_64          3.4.1.10-1.git.0.c96aed3.el7
atomic-openshift-node.x86_64            3.4.1.10-1.git.0.c96aed3.el7
atomic-openshift-pod.x86_64             3.4.1.10-1.git.0.c96aed3.el7
atomic-openshift-sdn-ovs.x86_64         3.4.1.10-1.git.0.c96aed3.el7
atomic-openshift-tests.x86_64           3.4.1.10-1.git.0.c96aed3.el7
...

更新安装包(在所有机器上执行)
yum update -y atomic-openshift-utils
yum update -y docker
yum install -y atomic-openshift-excluder atomic-openshift-docker-excluder

5. 删除 Registry 机器上的旧镜像(在 Registry 机器上执行)
删除本地(registry.access.redhat.com)的 core 镜像
REGISTRY="registry.access.redhat.com";PTH="openshift3";VERSION="v3.4.0.39";

docker rmi $REGISTRY/$PTH/ose:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-haproxy-router:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-deployer:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-sti-builder:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-docker-builder:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-pod:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-keepalived-ipfailover:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-docker-registry:$VERSION; \
docker rmi $REGISTRY/$PTH/ose-recycler:$VERSION; \
docker rmi $REGISTRY/$PTH/registry-console:$VERSION;

删除本地 Docker Registry(registry.example.com:5000)中的 core 镜像
REGISTRY="registry.example.com:5000";PTH="openshift3";VERSION="v3.4.0.39";

docker rmi $REGISTRY/$PTH/ose:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-haproxy-router:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-deployer:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-sti-builder:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-docker-builder:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-pod:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-keepalived-ipfailover:$VERSION ; \
docker rmi $REGISTRY/$PTH/ose-docker-registry:$VERSION; \
docker rmi $REGISTRY/$PTH/ose-recycler:$VERSION; \
docker rmi $REGISTRY/$PTH/registry-console:$VERSION;

删除本地(registry.access.redhat.com)的 logging 和 metrics 镜像镜像
REGISTRY="registry.access.redhat.com";PTH="openshift3";VERSION="v3.4";

docker rmi $REGISTRY/$PTH/logging-deployer:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-elasticsearch:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-kibana:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-fluentd:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-auth-proxy:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-curator:$VERSION; \
docker rmi $REGISTRY/$PTH/metrics-deployer:$VERSION; \
docker rmi $REGISTRY/$PTH/metrics-hawkular-metrics:$VERSION; \
docker rmi $REGISTRY/$PTH/metrics-cassandra:$VERSION; \
docker rmi $REGISTRY/$PTH/metrics-heapster:$VERSION;

删除本地 Docker Registry(registry.example.com:5000)中的 logging 和 metrics 镜像
REGISTRY="registry.example.com:5000";PTH="openshift3";VERSION="v3.4";

docker rmi $REGISTRY/$PTH/logging-deployer:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-elasticsearch:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-kibana:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-fluentd:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-auth-proxy:$VERSION; \
docker rmi $REGISTRY/$PTH/logging-curator:$VERSION; \
docker rmi $REGISTRY/$PTH/metrics-deployer:$VERSION; \
docker rmi $REGISTRY/$PTH/metrics-hawkular-metrics:$VERSION; \
docker rmi $REGISTRY/$PTH/metrics-cassandra:$VERSION; \
docker rmi $REGISTRY/$PTH/metrics-heapster:$VERSION;

删除本地(registry.access.redhat.com)的 apps 镜像
REGISTRY="registry.access.redhat.com";PTH="openshift3";

docker rmi $REGISTRY/jboss-webserver-3/webserver30-tomcat7-openshift:latest; \
docker rmi $REGISTRY/jboss-webserver-3/webserver30-tomcat8-openshift:latest; \
docker rmi $REGISTRY/jboss-eap-6/eap64-openshift:latest; \
docker rmi $REGISTRY/jboss-eap-7/eap70-openshift:latest; \

docker rmi $REGISTRY/jboss-amq-6/amq62-openshift:latest; \
docker rmi $REGISTRY/jboss-fuse-6/fis-java-openshift:latest; \
docker rmi $REGISTRY/jboss-fuse-6/fis-karaf-openshift:latest; \
docker rmi $REGISTRY/jboss-processserver-6/processserver63-openshift:latest; \
docker rmi $REGISTRY/jboss-decisionserver-6/decisionserver63-openshift:latest; \

docker rmi $REGISTRY/rhscl/mongodb-32-rhel7:latest; \
docker rmi $REGISTRY/rhscl/mysql-56-rhel7:latest; \
docker rmi $REGISTRY/rhscl/mysql-57-rhel7:latest; \
docker rmi $REGISTRY/rhscl/php-56-rhel7:latest; \
docker rmi $REGISTRY/rhscl/php-70-rhel7:latest; \
docker rmi $REGISTRY/rhscl/python-35-rhel7:latest; \
docker rmi $REGISTRY/rhscl/redis-32-rhel7:latest; \
docker rmi $REGISTRY/rhscl/ruby-23-rhel7:latest; \
docker rmi $REGISTRY/rhscl/s2i-base-rhel7:latest; \

docker rmi $REGISTRY/$PTH/jenkins-1-rhel7:latest; \
docker rmi $REGISTRY/$PTH/jenkins-2-rhel7:latest; \
docker rmi $REGISTRY/$PTH/jenkins-slave-base-rhel7:latest; \
docker rmi $REGISTRY/$PTH/jenkins-slave-maven-rhel7:latest; \
docker rmi $REGISTRY/$PTH/jenkins-slave-nodejs-rhel7:latest; \
docker rmi $REGISTRY/$PTH/nodejs-010-rhel7:latest;

删除本地的 Docker Registry(registry.example.com:5000)中的 apps 镜像
REGISTRY="registry.example.com:5000";PTH="openshift3";

docker rmi $REGISTRY/jboss-webserver-3/webserver30-tomcat7-openshift:latest; \
docker rmi $REGISTRY/jboss-webserver-3/webserver30-tomcat8-openshift:latest; \
docker rmi $REGISTRY/jboss-eap-6/eap64-openshift:latest; \
docker rmi $REGISTRY/jboss-eap-7/eap70-openshift:latest; \

docker rmi $REGISTRY/jboss-amq-6/amq62-openshift:latest; \
docker rmi $REGISTRY/jboss-fuse-6/fis-java-openshift:latest; \
docker rmi $REGISTRY/jboss-fuse-6/fis-karaf-openshift:latest; \
docker rmi $REGISTRY/jboss-processserver-6/processserver63-openshift:latest; \
docker rmi $REGISTRY/jboss-decisionserver-6/decisionserver63-openshift:latest; \

docker rmi $REGISTRY/rhscl/mongodb-32-rhel7:latest; \
docker rmi $REGISTRY/rhscl/mysql-56-rhel7:latest; \
docker rmi $REGISTRY/rhscl/mysql-57-rhel7:latest; \
docker rmi $REGISTRY/rhscl/php-56-rhel7:latest; \
docker rmi $REGISTRY/rhscl/php-70-rhel7:latest; \
docker rmi $REGISTRY/rhscl/python-35-rhel7:latest; \
docker rmi $REGISTRY/rhscl/redis-32-rhel7:latest; \
docker rmi $REGISTRY/rhscl/ruby-23-rhel7:latest; \
docker rmi $REGISTRY/rhscl/s2i-base-rhel7:latest; \

docker rmi $REGISTRY/$PTH/jenkins-1-rhel7:latest; \
docker rmi $REGISTRY/$PTH/jenkins-2-rhel7:latest; \
docker rmi $REGISTRY/$PTH/jenkins-slave-base-rhel7:latest; \
docker rmi $REGISTRY/$PTH/jenkins-slave-maven-rhel7:latest; \
docker rmi $REGISTRY/$PTH/jenkins-slave-nodejs-rhel7:latest; \

docker rmi $REGISTRY/$PTH/nodejs-010-rhel7:latest;

确认 Registry 机器上没有任何镜像
docker images

6. 上传新的镜像(在 MAC 机器上做)
scp ose-images-core-v3.4.tar.gz root@192.168.56.112:/opt/ose/images/
scp ose-images-logging_metric-v3.4.tar.gz root@192.168.56.112:/opt/ose/images/
scp ose-images-apps-latest_20170402.tar.gz root@192.168.56.112:/opt/ose/images/

7. 加载并推送新的镜像(在 Registry 机器上做)
加载镜像
cd /opt/ose/images/
for i in `ls *.tar.gz` ; do docker load -i $i; done;
说明:如果硬盘空间紧张,建议一个一个做,做完一个删除一个。

推送镜像到本地Docker Registry
REDHAT_REG="registry.access.redhat.com";
PRIVATE_REG="registry.example.com:5000";
for i in $(docker images|grep $REDHAT_REG|awk '{print $1":"$2}') ; do docker tag  $i "$PRIVATE_REG$(echo $i|awk -F 'com' {'print $2'})" ; done;
for i in `docker images|grep $PRIVATE_REG|awk '{print $1":"$2}'` ; do  docker push $i; done;

对于 core 镜像,还必须重新打上详细版本的 Tag,否则部署时会报告找不到这些镜像。
REGISTRY="registry.example.com:5000";PTH="openshift3";VERSION1="v3.4";VERSION2="v3.4.1.10";

docker tag $REGISTRY/$PTH/ose:$VERSION1 $REGISTRY/$PTH/ose:$VERSION2
docker push $REGISTRY/$PTH/ose:$VERSION2

docker tag $REGISTRY/$PTH/ose-haproxy-router:$VERSION1 $REGISTRY/$PTH/ose-haproxy-router:$VERSION2
docker push $REGISTRY/$PTH/ose-haproxy-router:$VERSION2

docker tag $REGISTRY/$PTH/ose-deployer:$VERSION1 $REGISTRY/$PTH/ose-deployer:$VERSION2
docker push $REGISTRY/$PTH/ose-deployer:$VERSION2

docker tag $REGISTRY/$PTH/ose-sti-builder:$VERSION1 $REGISTRY/$PTH/ose-sti-builder:$VERSION2
docker push $REGISTRY/$PTH/ose-sti-builder:$VERSION2

docker tag $REGISTRY/$PTH/ose-docker-builder:$VERSION1 $REGISTRY/$PTH/ose-docker-builder:$VERSION2
docker push $REGISTRY/$PTH/ose-docker-builder:$VERSION2

docker tag $REGISTRY/$PTH/ose-pod:$VERSION1 $REGISTRY/$PTH/ose-pod:$VERSION2
docker push $REGISTRY/$PTH/ose-pod:$VERSION2

docker tag $REGISTRY/$PTH/ose-keepalived-ipfailover:$VERSION1 $REGISTRY/$PTH/ose-keepalived-ipfailover:$VERSION2
docker push $REGISTRY/$PTH/ose-keepalived-ipfailover:$VERSION2

docker tag $REGISTRY/$PTH/ose-docker-registry:$VERSION1 $REGISTRY/$PTH/ose-docker-registry:$VERSION2
docker push $REGISTRY/$PTH/ose-docker-registry:$VERSION2

docker tag $REGISTRY/$PTH/ose-recycler:$VERSION1 $REGISTRY/$PTH/ose-recycler:$VERSION2
docker push $REGISTRY/$PTH/ose-recycler:$VERSION2

docker tag $REGISTRY/$PTH/registry-console:$VERSION1 $REGISTRY/$PTH/registry-console:$VERSION2
docker push $REGISTRY/$PTH/registry-console:$VERSION2

8. 将 atomic-openshift 相关包从 yum 排除列表中移除(在所有机器上执行)
atomic-openshift-excluder unexclude

9. 编辑、确认 ansible 的 inventory 文件(在 Master 机器上执行)
cat /etc/ansible/hosts
输出如下:
[OSEv3:children]
masters
nodes

[OSEv3:vars]
ansible_ssh_user=root
deployment_type=openshift-enterprise
openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', 'challenge': 'true', 'kind': 'HTPasswdPasswordIdentityProvider', 'filename': '/etc/origin/master/htpasswd'}]

[masters]
master.example.com

[nodes]
master.example.com
node1.example.com
node2.example.com

开始升级
ansible-playbook -i /etc/ansible/hosts \
 /usr/share/ansible/openshift-ansible/playbooks/byo/openshift-cluster/upgrades/v3_4/upgrade.yml
大约经过 10 分钟,最后输出如下:
......
PLAY RECAP *********************************************************************
localhost                  : ok=35   changed=3    unreachable=0    failed=0   
master.example.com         : ok=332  changed=48   unreachable=0    failed=0   
node1.example.com          : ok=101  changed=12   unreachable=0    failed=0   
node2.example.com          : ok=101  changed=12   unreachable=0    failed=0   

10. 将 atomic-openshift 相关包添加至 yum 排除列表(在所有机器上执行)
atomic-openshift-excluder exclude

11. 重启所有机器

12. 修改 docker 配置文件并重启 docker(在所有机器上执行)
cp /etc/sysconfig/docker /etc/sysconfig/docker.bak.$(date "+%Y%m%d%H%M%S");
sed -i s/".*OPTIONS=.*"/"OPTIONS='--selinux-enabled --insecure-registry 172.30.0.0\/16 --insecure-registry registry.example.com:5000'"/g /etc/sysconfig/docker;
sed -i 's/registry.access.redhat.com/registry.example.com:5000/g' /etc/sysconfig/docker;
systemctl restart docker

13. 确认升级成功(在 Master 机器上执行)
oc get node
输出如下:
NAME                 STATUS                     AGE
master.example.com   Ready,SchedulingDisabled   71d
node1.example.com    Ready                      71d
node2.example.com    Ready                      71d

oc get -n default dc/docker-registry -o json | grep \"image\"
输出如下:
"image": "openshift3/ose-docker-registry:v3.4.1.10",

oc get -n default dc/ose-router -o json | grep \"image\"
输出如下:
"image": "openshift3/ose-haproxy-router:v3.4.1.10",

确认 default project 中 ose-router 和 docker-registry pod 启动成功
oc project default
oc get pod
输出如下:
NAME                      READY     STATUS    RESTARTS   AGE
docker-registry-2-dgyaa   1/1       Running   0          56s
ose-router-2-5xeuo        1/1       Running   0          57s

创建创建一个用户并分配权限,然后登录控制台,快速创建一个 PHP 应用
htpasswd -cb /etc/origin/master/htpasswd redhat welcome1
oc adm policy add-cluster-role-to-user cluster-admin redhat
请参考《离线安装 OCP 3.4 之测试 PHP 应用》。

14. 删除 Node1 和 Node2 机器上的旧镜像(在 Node1 和 Node2 机器上执行)
docker rmi registry.example.com:5000/openshift3/ose-haproxy-router:v3.4.0.39
docker rmi registry.example.com:5000/openshift3/ose-deployer:v3.4.0.39
docker rmi registry.example.com:5000/openshift3/ose-docker-registry:v3.4.0.39
docker rmi registry.example.com:5000/openshift3/ose-pod:v3.4.0.39

15. 删除原有的 Image Stream,导入新的 Image Stream

删除原有的 Image Stream
for i in $(oc get is -n openshift --no-headers|awk '{print $1}') ; do oc delete is $i -n openshift; done;

创建指向本地 Docker Registry 的 Image Stream
REDHAT_REG="registry.access.redhat.com";
PRIVATE_REG="registry.example.com:5000";

sed s/"${REDHAT_REG}"/"${PRIVATE_REG}"/g  /usr/share/openshift/examples/image-streams/image-streams-rhel7.json |sed '/"creationTimestamp": null/a\\t,"annotations": {"openshift.io/image.insecureRepository": "true"}' |oc create -n openshift -f - ;

重新导入 Image
for i in $(oc get is -n openshift --no-headers|awk '{print $1}'); do oc import-image $i --insecure  -n openshift;done

16. 使用 oc adm diagnostics 检测 OpenShift(在 Master 机器上执行)
oc adm diagnostics
大约需要等待 3 分钟,因为需要创建 2 个 network project 验证网络,输出如下(只显示有错误的部分):
......
ERROR: [DCli0014 from diagnostic ConfigContexts@openshift/origin/pkg/diagnostics/client/config_contexts.go:285]
       For client config context '/master-example-com:8443/redhat':
       The server URL is 'https://master.example.com:8443'
       The user authentication is 'redhat/master-example-com:8443'
       The current project is 'default'
       (*errors.StatusError) the server has asked for the client to provide credentials
       
       This means that when we tried to make a request to the master API
       server, the request required credentials that were not presented. This
       can happen with an expired or invalid authentication token. Try logging
       in with this user again.
......
ERROR: [DNet2005 from diagnostic NetworkCheck@openshift/origin/pkg/diagnostics/network/run_pod.go:117]
       Setting up test environment for network diagnostics failed: Failed to run network diags test pod and service: [timed out waiting for the condition, timed out waiting for the condition]
......

oc login -u redhat -p welcome1
第 1 个错误没有了,第 2 个错误还在,第 2 个错误跟.Net 有关,可以忽略。

参考文献:
1. https://bbs.archlinux.org/viewtopic.php?id=208295