如何仅更新容器?

时间:2018-05-15 11:20:32

标签: kubernetes

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

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

这太浪费时间有效地工作了。

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

4 个答案:

答案 0 :(得分: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

每次更新部署时,它都会通过创建新的ReplicaSet,将其扩展为3个副本以及将旧的ReplicaSet缩减为0来更新Pod。如果在上一个正在进行的更新期间再次更新部署,则会开始创建立即新建ReplicaSet,无需等待上次更新完成。

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

如果您在编辑部署时输入了拼写错误(例如,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创建此部署时记录了命令,所以我们可以很容易地看到我们在每个修订版中所做的更改。

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

$ 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

有关详细信息,请阅读与Deployment

相关的Kubernetes文档部分

答案 1 :(得分:0)

首先,在您的前端,使用DNS名称而不是IP地址来访问您的后端。这样可以避免每次部署后端时重建前端应用程序。

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

最后,只要我理解,您的应用程序和数据库都在同一个Pod中。这不是一个好习惯,您应该将它们分开,以便在部署新版本的代码时不会导致数据库停机。

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

答案 2 :(得分:0)

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

kubectl set image deployment / helloworld-deployment basicnodeapp = buildmystartup / basicnodeapp:2

以下是我在生产AWS中使用wordpress做一个状态完整的应用程序的示例。

###############################################################################
#
# 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

答案 3 :(得分:0)

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

  1. 不要重启服务。仅重新启动您的pod。然后该服务不会更改其IP地址。
  2. 数据库和您的应用程序不需要是同一个pod中的2个容器。它们应该是2个独立的豆荚。然后,您需要另一个服务来将数据库公开给您的应用程序。
  3. 所以最终解决方案应该是这样的:

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