Link Search Menu Expand Document

Docker Swarm - いち

Nội dung

  1. Khái niệm và kiến trúc Docker Swarm
  2. Cài đặt Docker Swarm trên EC2s
  3. Swarm Monitoring
  4. HelloWorld Service
  5. AspNetCore Service

Một trong những lợi ích quan trọng của Docker là khả năng đóng gói hoạt động của một dịch vụ và tách biệt sự phụ thuộc vào môi trường hệ điều hành hay phần cứng bên dưới. Điều này giúp việc scale up khả năng xử lý dịch vụ được thực hiện thuận lợi hơn khi chúng ta có thể gia tăng số lượng Docker Container tương ứng với dịch vụ đó.

Trong ví dụ trong phần thực hành Scale Service Instances, chúng ta đã sử dụng công cụ Docker Compose để triển khai và điều phối đồng thời nhiều Container dựa trên các khai báo trong docker-compose.yml file. Tuy nhiên, Docker Compose chỉ áp dụng được trên một Docker Host duy nhất, điều này tạo nên sự giới hạn về mặt cấu hình phần cứng mà hệ thống Docker Host đó có thể cung cấp. Để khắc phục điều này, có rất nhiều các công cụ điều phối - Orchestration Tools cho phép các kĩ sư có thể kết nối và phối hợp nhiều máy tính vật lý / hoặc máy ảo để tạo nên mạng lưới - Cluster, bao gồm nhiều Docker Hosts - Nodes giúp việc scaling không còn bị giới hạn về mặt cấu hình phần cứng.

Trong bài viết này, chúng ta sẽ tìm hiểu một trong những Orchestration Tools phổ biến - Docker Swarm.


Khái niệm và kiến trúc Docker Swarm

Theo định nghĩa trong Swarm Key-Concepts, Docker Swarm là tập hợp - cluster các Docker Hosts thực thi trong chế độ swarm mode. Mỗi Docker Hosts là một Node trong cluster, có một trong số các vai trò sau:

  • Managers: quản lý và phân phối công việc yêu cầu đến Worker Nodes.
  • Leader: logging các hoạt động và quản lý trạng thái của Swarm
  • Workers: thực thi và báo cáo kết quả công việc đến Manager Nodes.

Tương tự như Docker Compose, một ứng dụng / dịch vụ sử dụng service definition để khai báo thông tin về Image, số lượng Containers - replicas, network, ports và storages. Dựa trên khai báo này, manager nodes chia tách service thành các đơn vị tasks, phân phối đến work nodes. Nếu một worker node không hoạt động vì một lý do nào đó, manager node sẽ điều hướng tasks sang những workder node khác để đảm bảo trạng thái yêu cầu của dịch vụ.

Swarm Ouput

Định nghĩa

Swarm định nghĩa Task là một container đang thực thi một phần công việc yêu cầu trong swarm service, và đặt dưới sự quản lý của manager node. Khái niệm này khác với standalone container khi hoạt động của container đó độc lập với bất kì cluster nào.

Dựa trên cách quản lý tasks, Swarm phân chia 2 loại service models:

  • Replicated service: task thực hiện trên nhiều nodes tuỳ theo giá trị scale
  • Global service: task phân phối đến mọi nodes trong cluster.

Swarm cũng có khả năng triển khai và quản lý đồng thời nhiều services trong một Stack. Các services được khai báo và cấu hình trong một file docker-compose.yml với cấu trúc tương tự như Docker Compose Tool mặc dù có những đặc điểm khác nhau, ví dụ: Swarm không hỗ trợ build command từ Dockerfile và mọi image đều phải liên kết với Docker Registry.


Cài đặt Docker Swarm trên EC2s

Để tạo Docker Swarm, chúng ta có thể sử dụng máy tính vật lý, máy ảo, AWS EC2 Instances để cài đặt Docker Hosts. Trong các ví dụ của Swarm, chúng ta sẽ sử dụng các EC2 Instances trong môi trường AWS VPC.

Các bước thức hiện:

Bước 1: Cài đặt VPC và EC2 Instances

Để cài đặt EC2 Instances trong một AWS VPN, chúng ta sẽ sử dụng ví dụ HelloCdk trong Cloud Development Kit.

Source code của VpcCdkStack.cs:

using Amazon.CDK;
using Amazon.CDK.AWS.EC2;
using Amazon.CDK.AWS.IAM;

namespace HelloCdk
{
    public class VpcCdkStack : Stack
    {
        public VpcCdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            // Create VPC and Public Subnets
            var subnets = new ISubnetConfiguration[]
            {
                new SubnetConfiguration { CidrMask = 24, Name = "CdkPublicSubnet", SubnetType = SubnetType.PUBLIC },
                new SubnetConfiguration { CidrMask = 24, Name = "CdkPrivateSubnet", SubnetType = SubnetType.PRIVATE }
            };
            var vpc = new Vpc(this, "CdkVpc", new VpcProps
            {
                Cidr = "10.0.0.0/16",
                EnableDnsHostnames = true,
                EnableDnsSupport = true,
                MaxAzs = 2,
                SubnetConfiguration = subnets,
            });
            
            var ec2SecurityGroup = new SecurityGroup(this, "CdkEC2Sg", new SecurityGroupProps
            {
                Vpc = vpc,
                Description = "EC2 Instance Security Group",
                AllowAllOutbound = true,
            });
            ec2SecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.IcmpPing(), "Allowing ping check");
            ec2SecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.Tcp(22), "Allowing ssh access");
            ec2SecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.Tcp(888), "Swarmpit administration");
            ec2SecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.Tcp(2377), "Allow cluster management communication");
            ec2SecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.Tcp(7946), "Allow communication among nodes");
            ec2SecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.Udp(7946), "Allow communication among nodes");
            ec2SecurityGroup.AddIngressRule(Peer.AnyIpv4(), Port.Udp(4789), "Overlay network traffic");
            
            // Create EC2 Instances
            var amznLinux = MachineImage.LatestAmazonLinux(new AmazonLinuxImageProps
            {
                Generation = AmazonLinuxGeneration.AMAZON_LINUX_2,
                Edition = AmazonLinuxEdition.STANDARD,
                Virtualization = AmazonLinuxVirt.HVM,
                Storage = AmazonLinuxStorage.GENERAL_PURPOSE,
                CpuType = AmazonLinuxCpuType.X86_64
            });

            for (int i = 0; i < vpc.AvailabilityZones.Length; i++)
            {
                var ec2Instance = new Instance_(this, $"CdkEc2Instance_{i}", new Amazon.CDK.AWS.EC2.InstanceProps
                {
                    Vpc = vpc,
                    VpcSubnets = new SubnetSelection
                    {
                        SubnetType = SubnetType.PUBLIC
                    },
                    SecurityGroup = ec2SecurityGroup,
                    MachineImage = amznLinux,
                    AvailabilityZone = vpc.AvailabilityZones[i],
                    InstanceType = new InstanceType("t2.micro"),
                    KeyName = "FriendReminders",
                    InitOptions = new ApplyCloudFormationInitOptions
                    {
                        PrintLog = true,
                        ConfigSets = new string[] { "default" }
                    },
                    Init = CloudFormationInit.FromElements(new InitElement[]
                    {
                        InitPackage.Yum("git"),
                        InitPackage.Yum("docker"),
                        InitService.Enable("docker")
                    })
                });
                ec2Instance.UserData.AddCommands("sudo yum update -y");
                ec2Instance.UserData.AddCommands("sudo yum install - y postgresql");
                ec2Instance.UserData.AddCommands("sudo usermod -a -G docker ec2-user");
            }
        }
    }
}

Chú ý: Trong VpcCdkStack, chúng ta bổ sung security rules cho phép mở các cổng cần thiết cho hoạt động của Swarm Nodes:

  • TCP Port 2377: hoạt động giao tiếp quản lý Cluster
  • TCP/UDP Port 7946: giao tiếp giữa các Docker Nodes
  • UDP Port 4789: sử dụng cho overlay network traffic
  • TCP Port 888: sử dụng bởi công cụ Swarmpit

Trong Program.cs, khởi tạo VpcCdkStack:

using Amazon.CDK;

namespace HelloCdk
{
    sealed class Program
    {
        public static void Main(string[] args)
        {
            var app = new App();
            new VpcCdkStack(app, "VpcCdkStack");
            app.Synth();
        }
    }
}

Sử dụng lệnh cdk deploy trong folder gốc của project HelloCdk:

CDK EC2 Deploys Ouput

Xác nhận y để triển khai Stack. Kết quả trả về giá trị Stack ID

VpcCdkStack: deploying...
VpcCdkStack: creating CloudFormation changeset...
[██████████████████████████████████████████████████████████] (32/32)

 ✅  VpcCdkStack

Stack ARN:
arn:aws:cloudformation:ap-southeast-2:729365137003:stack/VpcCdkStack/a26754a0-f8a3-11ea-bd4e-0a576a435132

Sử dụng AWS Console, danh sách EC2 Instances hiển thị các EC2 trong VpcCdkStack:

  • VpcCdkStack/CdkEc2Instance_0
  • VpcCdkStack/CdkEc2Instance_1

CDK EC2 List Ouput

Bước 2: Cài đặt Swarm, Manager và Worker Nodes

  • Sử dụng SSH với keypair FrienFriendReminders.pem để connect đến VpcCdkStack/CdkEc2Instance_0:
 ssh -i "FriendReminders.pem" ec2-user@ec2-3-106-113-26.ap-southeast-2.compute.amazonaws.com
  • Kết hợp lệnh docker info để kiếm tra môi trường Docker. Kết quả câu lệnh cho chúng ta biết một số thông tin:
    • Số lượng Containers với các trạng thái Running, Paused, Stopped
    • Operating System: Amazon Linux AMI 2018.03
    • CPU, Memor, Docker Registry etc..
    • Trạng thái Swarm: inactive

CDK EC2 List Ouput

  • Khởi tạo Swarm với lệnh
docker swarm init

Kết quả hiển thị cung cấp thông tin câu lệnh và giá trị Token được sử dụng để các Docker Node khác có thể tham gia Swarm

Swarm initialized: current node (9in81mu2sir9nk9jf1vhqafmm) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-3k0k0wutv9msvroxdcl6r928ao0wcctflccnzt1lb7z8swibvw-2srq16r7f2e3aw59422gsuuw3 10.0.0.165:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

Giá trị 10.0.0.165 là Private IP Address của EC2 Instance trong AWS VPC.

  • Sử dụng lại câu lệnh docker info để kiểm tra trạng thái Swarm active

CDK EC2 Swarm Ouput

  • Đăng nhập EC2 Instance VpcCdkStack/CdkEc2Instance_1 với SSH Connect và liên kết đến Docker Swarm theo lệnh
docker swarm join --token SWMTKN-1-3k0k0wutv9msvroxdcl6r928ao0wcctflccnzt1lb7z8swibvw-2srq16r7f2e3aw59422gsuuw3 10.0.0.165:2377

CDK EC2 Swarm Worker

  • Với các bước thực hiện trên, chúng ta đã xây dựng một Docker Swarm với 2 Nodes:
    • Worker: VpcCdkStack/CdkEc2Instance_1 - Private IP 10.0.0.165
    • Manager (Leader): VpcCdkStack/CdkEc2Instance_0 - Private IP 10.0.1.173
  • Trên Terminal của Manager Node, hiển thị danh sách Nodes trong Cluster với lệnh
docker node ls

CDK Nodes List Ouput

Chú ý

EC2 Instance VpcCdkStack/CdkEc2Instance_0 có hai địa chỉ IP ( và DNS Name)

  • Public IP Address: 3.106.113.26
  • Private IP Address: 10.0.0.165

Trong phần tiếp theo, chúng ta sẽ sử dụng EC2 Instance này làm vai trò Node Manager trong Cluster. Các Worker Node sẽ tham gia vào Cluster dựa trên giá trị Private IP và Cluster Token của Manager Node. Trong khi đó, giá trị Public IP sẽ sử dụng để truy cập cho các hoạt động quản lý Swarm Cluster với phần mềm Swarmkit hoặc Swarmprom.


Swarm Monitoring

Swarmpit cung cấp giao diện đơn giản nhưng khá rõ ràng các thông tin cần thiết để giám sát các hoạt động trong Docker Swarm Cluster. Có thể sử dụng Swarmpit để quản lý các stacks, services, secrets, volumes, network etc..hoặc liên kết với Docker Registry (Docker Hub, AWS ECR) pull về Images và triển khai trong Cluster. Tham khảo chi tiết chức năng của Swarmpit: Swarmpit ROADMAP

Để cài đặt Swarmpit, đăng nhập vào EC2 Instance - Manager Node, và thực hiện lệnh

docker run -it --rm \
  --name swarmpit-installer \
  --volume /var/run/docker.sock:/var/run/docker.sock \
  swarmpit/install:edge

Hướng dẫn

Theo mặc đinh Swarmpit Admin sử dụng Port 888, trong VpcCdkStack chúng ta đã sử dụng ec2SecurityGroup construct để thực hiện việc cấu hình Port này.

Swarmpit Installer Ouput

Xác nhận các thông tin cài đặt:

  • Stackname [swarmpit]
  • Application Port [888]
  • Database volume driver [local]
  • Admin username [admin]
  • Admin password: password

Sử dụng địa chỉ Public IP 3.106.113.26 của Manager Node (hoặc giá trị DNS ec2-3-106-113-26.ap-southeast-2.compute.amazonaws.com), truy cập địa chỉ 3.106.113.26:888.

Sau khi đăng nhập với username / password, chúng ta có kết quả giao diện màn hình chính của Swarmpit

Swarmpit UI Ouput


HelloWorld Service

Trong phần này, chúng ta sẽ sử dụng một số câu lệnh docker trong Terminal của Manager Node để thao tác với services trong môi trường Swarm.

  • Liệt kê services

Sau khi hoàn tất cài đặt Swarmpit trong phần trước, chúng ta sử dụng lệnh docker service ls để liệt kê danh sách services đang thực thi trong Swarm

Swarm Services Ouput

Bên cạnh đó, docker service --help cung cấp các câu lệnh có thể sử dụng để quản lý services.

  • Tạo service mới
docker service create --replicas 5 --name helloworld alpine ping docker.com

Tham số trong câu lệnh trên

  • replicas: số lượng Container mong muốn để thực thi service
  • name: tên của service
  • alpine ping docker.com: chỉ định Docker Image và câu lệnh thực thi trong Container tương ứng.

Kết quả câu lệnh khi thành công trả về giá trị Hash ID của service mới tạo ra:

a9wgmjh77r0lbspl0tdm2n7nc
overall progress: 5 out of 5 tasks 
1/5: running   [==================================================>] 
2/5: running   [==================================================>] 
3/5: running   [==================================================>] 
4/5: running   [==================================================>] 
5/5: running   [==================================================>] 
verify: Service converged
  • Kiểm tra thông tin service

Sử dụng giá trị Hash ID hoặc tên của service, chúng ta có thể kiểm tra thông tin chi tiết trong một service theo lệnh

docker service inspect --pretty helloworld

Output:

ID:		a9wgmjh77r0lbspl0tdm2n7nc
Name:		helloworld
Service Mode:	Replicated
 Replicas:	5
Placement:
UpdateConfig:
 Parallelism:	1
 On failure:	pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Update order:      stop-first
RollbackConfig:
 Parallelism:	1
 On failure:	pause
 Monitoring Period: 5s
 Max failure ratio: 0
 Rollback order:    stop-first
ContainerSpec:
 Image:		alpine:latest@sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321
 Args:		ping docker.com 
 Init:		false
Resources:
Endpoint Mode:	vip

Để kiểm tra những Node đang thực thi service, sử dụng lệnh

docker service ps helloworld

Kết quả cho thấy service helloworld được thực thi trong những Containers trên Manager và Work Node cùng trạng thái Running

ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
ca93soaooq7w        helloworld.1        alpine:latest       ip-10-0-1-173       Running             Running about a minute ago                       
lrkn7drat5sq        helloworld.2        alpine:latest       ip-10-0-0-165       Running             Running about a minute ago                       
c6hh05wl20lq        helloworld.3        alpine:latest       ip-10-0-1-173       Running             Running about a minute ago                       
tqrlgcxaw0eb        helloworld.4        alpine:latest       ip-10-0-0-165       Running             Running about a minute ago                       
fhe45uzjl0d5        helloworld.5        alpine:latest       ip-10-0-0-165       Running             Running about a minute ago                       
[ec2-user@ip-10-0-0-165 ~]$ 

Dựa trên giá trị ID của Container, có thể sử dụng lệnh docker inspect:

docker inspect ca93soaooq7w

Output:

[
    {
        "ID": "ca93soaooq7wi1eu10tip3aqn",
        "Version": {
            "Index": 355
        },
        "CreatedAt": "2020-09-18T04:07:47.060178227Z",
        "UpdatedAt": "2020-09-18T04:07:48.035561581Z",
        "Labels": {},
        "Spec": {
            "ContainerSpec": {
                "Image": "alpine:latest@sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321",
                "Args": [
                    "ping",
                    "docker.com"
                ],
                "Init": false,
                "DNSConfig": {},
                "Isolation": "default"
            },
            "Resources": {
                "Limits": {},
                "Reservations": {}
            },
            "Placement": {...},
            "ForceUpdate": 0
        },
        "ServiceID": "a9wgmjh77r0lbspl0tdm2n7nc",
        "Slot": 1,
        "NodeID": "6pe95mx4tbslg7htrvw6zcgd5",
        "Status": {
            "Timestamp": "2020-09-18T04:07:48.011679995Z",
            "State": "running",
            "Message": "started",
            "ContainerStatus": {
                "ContainerID": "6ddc790fd5080a37bd8f869e262254240f4e5ad5bf07d728cc0de0e19e632737",
                "PID": 15101,
                "ExitCode": 0
            },
            "PortStatus": {}
        },
        "DesiredState": "running"
    }
]
  • Thay đổi giá trị scale

Để cập nhật số lượng Containers thưc thi service, sử dụng lệnh:

docker service scale helloworld=10

Output:

helloworld scaled to 10
overall progress: 10 out of 10 tasks 
1/10: running   [==================================================>] 
2/10: running   [==================================================>] 
3/10: running   [==================================================>] 
4/10: running   [==================================================>] 
5/10: running   [==================================================>] 
6/10: running   [==================================================>] 
7/10: running   [==================================================>] 
8/10: running   [==================================================>] 
9/10: running   [==================================================>] 
10/10: running   [==================================================>] 
verify: Service converged 
  • Loại bỏ service khỏi Swarm: docker service rm helloworld

AspNetCore Service

Tương tự helloworld service, trong phần này chúng ta nâng cao phần thực hành service bằng việc sử dụng Docker Image .NET Core Samples từ Docker Hub

Các bước thực hiện

Bước 1: Pull Image từ Docker Hub

docker pull mcr.microsoft.com/dotnet/core/samples

Bước 2: Cập nhật Security Group

Để truy cập ứng dụng .Net Core Sample, chúng ta cần cập nhật security group của EC2 Instances để mở cổng 8000.

  • Sử dụng AWS Console, trong danh sách EC2 Instances, lựa chọn một Instance bất kì trong Cluster:

Security Group Ouput

Security tab hiển thị liên kết với Security Group sử dụng bởi EC2 Instance. Lựa chọn liên kết để chuyển sang màn hình mới:

Security Group Edit Ouput

  • Lựa chọn button Edit Inbound Rules để cập nhật rule của Security

Security Group Add Ouput

  • Lựa chọn button Add Rule, cập nhật thông tin rule mới:
    • Type: Custom TCP
    • Port: 8000
    • Source: Anywhere

Click Save Rule button để lưu lại thay đổi

  • Danh sách Security Group sau khi thay đổi

Security Group List Ouput

Bước 3: Thực thi service

docker service create --replicas 2 --name aspnet_core_sample --publish 8000:80  mcr.microsoft.com/dotnet/core/samples:aspnetapp

Tham số --publish 8000:80 cho phép service trong Swarm truy cập từ môi trường bên ngoài qua port 8000.

Output

iy21c8nq60aavd137cl46utkk
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service converged 

Với giá trị Hash ID, sử dụng docker service ps iy21c8nq60aavd137cl46utkk để kiểm tra kết quả:

ID                  NAME                   IMAGE                                             NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
zqxznwvpa6zz        aspnet_core_sample.1   mcr.microsoft.com/dotnet/core/samples:aspnetapp   ip-10-0-1-173       Running             Running 28 minutes ago                       
wddfrruegy94        aspnet_core_sample.2   mcr.microsoft.com/dotnet/core/samples:aspnetapp   ip-10-0-0-165       Running             Running 28 minutes ago  

Từ Output này, chúng ta thấy .NET Core Sample đang thực thi trên 2 EC2 Instance của Swarm Cluster. Dựa vào giá trị Public IP của EC2 Instance và Port 8000, truy cập một trong các địa chỉ:

  • http://3.106.113.26:8000
  • http://3.104.55.22:8000

Kết quả hiển thị trên trình duyệt:

EC2 App Ouput

Bước 4: Cluster Clean Up

Để tiết kiệm các chi phí AWS, sau khi kết thúc mỗi bài thực hành với AWS CDK, chúng ta nên dọn dẹp Stack với lệnh

cdk destroy VpcCdkStack

Copyright © 2019-2022 Tuan Anh Le.