【问题标题】:How to update a container only?如何仅更新容器?
【发布时间】:2018-05-16 19:20:44
【问题描述】:

我在本地使用 minikube 工作,每次更改代码时,我都会删除服务(和部署)并创建一个新服务。

此操作为每个容器生成一个新 IP,因此我还需要更新我的前端,并在我的数据库容器中插入新数据,因为每次删除服务时我都会丢失所有数据。

高效工作太浪费时间了。

我想知道是否有一种方法可以在不生成新 IP 且不删除 pod 的情况下更新容器(因为我不想每次更新后端代码时都删除我的 db 容器)?

【问题讨论】:

    标签: kubernetes


    【解决方案1】:

    使用新图像更新现有的Deployment 很容易,无需删除它。

    假设我们有一个带有部署的 YAML file

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:1.7.9
            ports:
            - containerPort: 80
    

    要运行此部署,请运行以下命令:

    $ kubectl create -f nginx-deployment.yaml --record
    

    --record - 将当前命令附加到已创建或更新资源的注释中。这对于将来的审查很有用,例如调查在每个部署修订中执行了哪些命令,以及进行回滚。)

    要查看部署部署状态,请运行

    $ kubectl rollout status deployment/nginx-deployment
    

    要更新 nginx 镜像版本,只需运行以下命令:

    $ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
    

    或者您可以使用以下命令编辑现有部署:

    $ kubectl edit deployment/nginx-deployment
    

    要查看部署更新过程的状态,请运行以下命令:

    $ kubectl rollout status deployment/nginx-deployment
    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    deployment "nginx-deployment" successfully rolled out
    

    $ kubectl get deployments
    NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3         3         3            3           36s
    

    每次更新 Deployment 时,它都会通过创建新的 ReplicaSet,将其扩展为 3 个副本,并将旧的 ReplicaSet 缩减为 0 来更新 Pod。如果在上一次更新过程中再次更新 Deployment,它会开始创建立即新建 ReplicaSet,无需等待上一次更新完成。

    $ kubectl get rs
    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1180356465   3         3         3       4s
    nginx-deployment-2538420311   0         0         0       56s
    

    如果您在编辑 Deployment 时打错字(例如,nginx:1.91),您可以将其回滚到以前的好版本。
    首先,检查此部署的修订:

    $ kubectl rollout history deployment/nginx-deployment
    deployments "nginx-deployment"
    REVISION    CHANGE-CAUSE
    1           kubectl create -f nginx-deployment.yaml --record
    2           kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
    3           kubectl set image deployment/nginx-deployment nginx=nginx:1.91
    

    因为我们在使用 --record 创建此 Deployment 时记录了命令,所以我们可以很容易地看到我们在每个修订版中所做的更改。

    要查看每个修订的详细信息,请运行:

    $ kubectl rollout history deployment/nginx-deployment --revision=2
    deployments "nginx-deployment" revision 2
      Labels:       app=nginx
              pod-template-hash=1159050644
      Annotations:  kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
      Containers:
       nginx:
        Image:      nginx:1.9.1
        Port:       80/TCP
         QoS Tier:
            cpu:      BestEffort
            memory:   BestEffort
        Environment Variables:      <none>
      No volumes.
    

    现在您可以使用命令回滚到以前的版本:

    $ kubectl rollout undo deployment/nginx-deployment
    deployment "nginx-deployment" rolled back
    

    或者您可以回滚到特定版本:

    $ kubectl rollout undo deployment/nginx-deployment --to-revision=2
    deployment "nginx-deployment" rolled back
    

    更多信息请阅读Kubernetes文档中与Deployment相关的部分

    【讨论】:

    • kubectl set image 命令是我要找的!但奇怪的是我不能将docker buildkubectl set image命令放入shell脚本(我看到成功消息但容器没有改变,就像它是在另一个“环境”中构建的一样),任何想知道为什么?
    • 可能没有为运行脚本的用户帐户配置正确的身份验证。
    【解决方案2】:

    首先,在您的前端,使用 DNS 名称而不是 IP 地址来访问您的后端。这将使您无需在每次部署后端时都重新构建前端应用程序。

    话虽如此,没有必要仅仅为了部署新版本的后端而删除您的服务。实际上,您只需要更新您的部署,使其引用您使用后端的最新代码构建的新 docker 映像。

    最后,据我所知,您的应用程序和数据库都在同一个 Pod 中。这不是一个好的做法,您应该将它们分开,以免在部署新版本代码时导致数据库停机。

    作为旁注,不确定是否是这种情况,但如果您使用 minikube 作为开发环境,您可能做错了。您应该单独使用 docker 和卷绑定,但这超出了您的问题范围。

    【讨论】:

      【解决方案3】:

      使用 kops 并在 AWS 中的免费套餐上创建类似生产的集群。 为了解决这个问题,您需要确保为前端使用负载均衡器。为您的数据库容器创建一个暴露端口的服务,以便您的前端可以访问它,并将其放入您的前端清单中,使其保持静态。服务发现将处理 IP 地址,您的容器将自动连接到端口。您还可以为您的数据库设置持久存储。当你更新你的前端代码时,用它来更新你的容器,这样什么都不会改变。

      kubectl 设置镜像部署/helloworld-deployment basicnodeapp=buildmystartup/basicnodeapp:2

      以下是我将如何使用 wordpress 在生产 AWS 中创建一个状态完整的应用程序。

      ###############################################################################
      #
      # Creating a stateful app with persistent storage and front end containers
      #
      ###############################################################################
      
      * Here is how you create a stateful app using volumes and persistent storage for production.
      
      * To start off we can automate the storage volume creation for our mysql server with a storage object and persistent volume claim like so:
      
      $ cat storage.yml
      kind: StorageClass
      apiVersion: storage.k8s.io/v1beta1
      metadata:
        name: standard
      provisioner: kubernetes.io/aws-ebs
      parameters:
        type: gp2
        zone: us-east-1b
      
        $ cat pv-claim.yml
        kind: PersistentVolumeClaim
        apiVersion: v1
        metadata:
          name: db-storage
          annotations:
            volume.beta.kubernetes.io/storage-class: "standard"
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 8Gi
      
      * Lets go ahead and create these so they are ready for our deployment of mysql
      
      $ kubectl create -f storage.yml
      storageclass "standard" created
      
      
      $ kubectl create -f pv-claim.yml
      persistentvolumeclaim "db-storage" created
      
      * Lets also create our secrets file that will be needed for mysql and wordpress
      
      $ cat wordpress-secrets.yml
      apiVersion: v1
      kind: Secret
      metadata:
        name: wordpress-secrets
      type: Opaque
      data:
        db-password: cGFzc3dvcmQ=
        # random sha1 strings - change all these lines
        authkey: MTQ3ZDVhMTIzYmU1ZTRiMWQ1NzUyOWFlNWE2YzRjY2FhMDkyZGQ4OA==
        loggedinkey: MTQ3ZDVhMTIzYmU1ZTRiMWQ1NzUyOWFlNWE2YzRjY2FhMDkyZGQ4OQ==
        secureauthkey: MTQ3ZDVhMTIzYmU1ZTRiMWQ1NzUyOWFlNWE2YzRjY2FhMDkyZGQ5MQ==
        noncekey: MTQ3ZDVhMTIzYmU1ZTRiMWQ1NzUyOWFlNWE2YzRjY2FhMDkyZGQ5MA==
        authsalt: MTQ3ZDVhMTIzYmU1ZTRiMWQ1NzUyOWFlNWE2YzRjY2FhMDkyZGQ5Mg==
        secureauthsalt: MTQ3ZDVhMTIzYmU1ZTRiMWQ1NzUyOWFlNWE2YzRjY2FhMDkyZGQ5Mw==
        loggedinsalt: MTQ3ZDVhMTIzYmU1ZTRiMWQ1NzUyOWFlNWE2YzRjY2FhMDkyZGQ5NA==
        noncesalt: MTQ3ZDVhMTIzYmU1ZTRiMWQ1NzUyOWFlNWE2YzRjY2FhMDkyZGQ5NQ==
      
      $ kubectl create -f wordpress-secrets.yml
      
      
      
      * Take note of the names we assigned. We will need these for the mysql deployment
      
      * We created the storage in us-east-1b so lets set a node label for our node in that AZ so our deployment is pushed to that node and can attach our volume.
      
      $ kubectl label nodes ip-172-20-48-74.ec2.internal storage=mysql
      node "ip-172-20-48-74.ec2.internal" labeled
      
      * Here is our mysql pod definition. Notice at the bottom we use a nodeSelector
      * We will need to use that same one for our deployment so it can reach us-east-1b
      
      
      $ cat wordpress-db.yml
      apiVersion: v1
      kind: ReplicationController
      metadata:
        name: wordpress-db
      spec:
        replicas: 1
        selector:
          app: wordpress-db
        template:
          metadata:
            name: wordpress-db
            labels:
              app: wordpress-db
          spec:
            containers:
            - name: mysql
              image: mysql:5.7
              args:
                - "--ignore-db-dir=lost+found"
              ports:
              - name: mysql-port
                containerPort: 3306
              env:
                - name: MYSQL_ROOT_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: db-password
              volumeMounts:
              - mountPath: "/var/lib/mysql"
                name: mysql-storage
            volumes:
              - name: mysql-storage
                persistentVolumeClaim:
                  claimName: db-storage
            nodeSelector:
              storage: mysql
      
      * Before we go on to the deployment lets expose a service on port 3306 so wordpress can connect.
      
      $ cat wordpress-db-service.yml
      apiVersion: v1
      kind: Service
      metadata:
        name: wordpress-db
      spec:
        ports:
        - port: 3306
          protocol: TCP
        selector:
          app: wordpress-db
        type: NodePort
      
      
      
      $ kubectl create -f wordpress-db-service.yml
      service "wordpress-db" created
      
      * Now lets work on the deployment. We are going to use EFS to save all our pictures and blog posts so lets create that on us-east-1b also
      
      * So first lets create our EFS NFS share
      
      $ aws efs create-file-system --creation-token 1
      {
          "NumberOfMountTargets": 0,
          "SizeInBytes": {
              "Value": 0
          },
          "CreationTime": 1501863105.0,
          "OwnerId": "812532545097",
          "FileSystemId": "fs-55ed701c",
          "LifeCycleState": "creating",
          "CreationToken": "1",
          "PerformanceMode": "generalPurpose"
      }
      
      $ aws efs create-mount-target --file-system-id fs-55ed701c --subnet-id subnet-7405f010 --security-groups sg-ffafb98e
      {
          "OwnerId": "812532545097",
          "MountTargetId": "fsmt-a2f492eb",
          "IpAddress": "172.20.53.4",
          "LifeCycleState": "creating",
          "NetworkInterfaceId": "eni-cac952dd",
          "FileSystemId": "fs-55ed701c",
          "SubnetId": "subnet-7405f010"
      }
      
      
      * Before we launch the deployment lets make sure our mysql server is up and connected to the volume we created
      
      $ kubectl get pvc
      NAME         STATUS    VOLUME                                     CAPACITY   ACCESSMODES   STORAGECLASS   AGE
      db-storage   Bound     pvc-82c889c3-7929-11e7-8ae1-02fa50f1a61c   8Gi        RWO           standard       51m
      
      * ok status bound means our container is connected to the volume.
      
      * Now lets launch the wordpress frontend of two replicas.
      
      $ cat wordpress-web.yml
      apiVersion: extensions/v1beta1
      kind: Deployment
      metadata:
        name: wordpress-deployment
      spec:
        replicas: 2
        template:
          metadata:
            labels:
              app: wordpress
          spec:
            containers:
            - name: wordpress
              image: wordpress:4-php7.0
              # uncomment to fix perm issue, see also https://github.com/kubernetes/kubernetes/issues/2630
              # command: ['bash', '-c', 'chown', 'www-data:www-data', '/var/www/html/wp-content/upload', '&&', 'apache2', '-DFOREGROUND']
              ports:
              - name: http-port
                containerPort: 80
              env:
                - name: WORDPRESS_DB_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: db-password
                - name: WORDPRESS_AUTH_KEY
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: authkey
                - name: WORDPRESS_LOGGED_IN_KEY
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: loggedinkey
                - name: WORDPRESS_SECURE_AUTH_KEY
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: secureauthkey
                - name: WORDPRESS_NONCE_KEY
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: noncekey
                - name: WORDPRESS_AUTH_SALT
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: authsalt
                - name: WORDPRESS_SECURE_AUTH_SALT
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: secureauthsalt
                - name: WORDPRESS_LOGGED_IN_SALT
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: loggedinsalt
                - name: WORDPRESS_NONCE_SALT
                  valueFrom:
                    secretKeyRef:
                      name: wordpress-secrets
                      key: noncesalt
                - name: WORDPRESS_DB_HOST
                  value: wordpress-db
              volumeMounts:
              - mountPath: /var/www/html/wp-content/uploads
                name: uploads
            volumes:
            - name: uploads
              nfs:
                server: us-east-1b.fs-55ed701c.efs.us-east-1.amazonaws.com
                path: /
      
      * Notice we put together a string for the NFS share.
      * AZ.fs-id.Region.amazonaws.com
      
      * Now lets create our deployment.
      
      $ kubectl create -f wordpress-web.yml
      
      $ cat wordpress-web-service.yml
      apiVersion: v1
      kind: Service
      metadata:
        name: wordpress
      spec:
        ports:
        - port: 80
          targetPort: http-port
          protocol: TCP
        selector:
          app: wordpress
        type: LoadBalancer
      
      
      * And now the load balancer for our two nodes
      
      $ kubectl create -f wordpress-web-service.yml
      
      
      * Now lets find our ELB and create a Route53 DNS name for it.
      
      $ kubectl get services
      
      $ kubectl describe service wordpress
      Name:           wordpress
      Namespace:      default
      Labels:         <none>
      Annotations:        <none>
      Selector:       app=wordpress
      Type:           LoadBalancer
      IP:         100.70.74.90
      LoadBalancer Ingress:   acf99336a792b11e78ae102fa50f1a61-516654231.us-east-1.elb.amazonaws.com
      Port:           <unset> 80/TCP
      NodePort:       <unset> 30601/TCP
      Endpoints:      100.124.209.16:80,100.94.7.215:80
      Session Affinity:   None
      Events:
        FirstSeen LastSeen    Count   From            SubObjectPath   Type        Reason          Message
        --------- --------    -----   ----            -------------   --------    ------          -------
        38m       38m     1   service-controller          Normal      CreatingLoadBalancer    Creating load balancer
        38m       38m     1   service-controller          Normal      CreatedLoadBalancer Created load balancer
      
        $ kubectl get deployments
        NAME                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
        wordpress-deployment   2         2         2            2           2m
      
      
        $ kubectl get pods
        NAME                                    READY     STATUS              RESTARTS   AGE
        sysdig-agent-4sxv2                      1/1       Running             0          3d
        sysdig-agent-nb2wk                      1/1       Running             0          3d
        sysdig-agent-z42zj                      1/1       Running             0          3d
        wordpress-db-79z87                      1/1       Running             0          54m
        wordpress-deployment-2971992143-c8gg4   0/1       ContainerCreating   0          1m
        wordpress-deployment-2971992143-h36v1   1/1       Running             0          1m
      

      【讨论】:

        【解决方案4】:

        我认为你实际上需要解决 2 个问题:

        1. 不要重新启动服务。仅重新启动您的 pod。然后该服务不会更改其 IP 地址。
        2. 数据库和您的应用程序不需要是同一个 pod 中的 2 个容器。它们应该是 2 个独立的 pod。然后,您需要另一个服务将数据库公开给您的应用程序。

        所以最终的解决方案应该是这样的:

        1. 数据库 pod - 运行一次,在开发过程中从不重新启动。
        2. 数据库服务 - 创建一次,永不重启。
        3. 应用程序 pod - 这是您在应用程序代码更改时唯一需要重新加载的内容。它需要访问数据库,因此您在应用程序中按字面意思编写“database-service:3306”或类似的内容。这里的“database-service”是您在 (2) 中创建的服务的名称。
        4. 应用程序服务 - 创建一次,从不重新启动。您可以使用此服务的 IP 地址从 minikube 外部访问应用程序。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-08
          • 1970-01-01
          • 2015-03-26
          • 1970-01-01
          • 2021-05-24
          • 2017-01-29
          相关资源
          最近更新 更多