First, build a docker container that contains all required packages. In this example, I choose ubuntu:bionic as the basic image, and I installed other packages onto it.
FROM ubuntu:bionic
RUN apt-get update
RUN apt-get install python3 -y
RUN apt-get install python3-pip -y
RUN pip3 install grpcio
ADD app /app/
EXPOSE 22222
The Dockerfile
is shown above. In the app
file, it contains 4 files. They are:
client.py, server.py, test_pb2.py, test_pb2_grpc.py
test_pb2.py
and test_pb2_grpc.py
is generated by compiling the test.proto
file. The content for test.proto
is shown below.
syntax = "proto3";
package lmjwtest;
// service, encode a plain text
service EncodeService {
// request a service of encode
rpc GetEncode(plaintext) returns (encodetext) {}
}
message plaintext {
string pttransactionID = 1;
string ptproperties = 2;
string ptsenderID = 3;
}
message encodetext {
string enctransactionID = 1;
string encproperties = 2;
string encsenderID = 3;
}
By using the grpcio-tools to compile the test.proto
file, we can get the test_pb2.py
and test_pb2_grpc.py
two files.
The compile command is:
python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. test.proto
Then we create the server and client using the grpc generated python file.
The server.py file:
from concurrent import futures
import base64
import time
import test_pb2
import test_pb2_grpc
import grpc
def encoding(msg):
return base64.a85encode(msg.encode())
class EService(test_pb2_grpc.EncodeServiceServicer):
def GetEncode(self, request, context):
return test_pb2.encodetext(enctransactionID = encoding(request.pttransactionID),
encproperties = encoding(request.ptproperties),
encsenderID = request.ptsenderID)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=2))
test_pb2_grpc.add_EncodeServiceServicer_to_server(EService(),server)
server.add_insecure_port('[::]:22222')
server.start()
try:
while True:
time.sleep(60*60*24)
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
serve()
the client.py:
import grpc
import test_pb2
import test_pb2_grpc
def run():
channel = grpc.insecure_channel('server:22222')
stub = test_pb2_grpc.EncodeServiceStub(channel)
response = stub.GetEncode(test_pb2.plaintext(pttransactionID = 'abcde',
ptproperties = 'This is a plain text transaction',
ptsenderID = 'Will smith'))
print("Encdded service received:\n EnctransactionID:%s\n,Encproperties:%s\n,EncsenderID:%s\n"%(response.enctransactionID,response.encproperties,response.encsenderID))
if __name__ == "__main__":
run()
To test the grpc on the local host, we can open two terminal. The first terminal runs the server.py and the second runs the client.py. If the client.py can return the encoded message, it proves the grpc is working properly on the local host.
The Next step is to deploy this simple application on docker containers and the client and server need to be on different containers. So the encoding is considered as a microservice. To do this, we need to run two containers.
Using command docker build .
to generate the docker images from the dockerfile. Note that you need to create an app folder and copy the “client.py, server.py, test_pb2.py, test_pb2_grpc.py” into this folder.
the file tree should look like this
-somename
-Dockerfile
-app/
-client.py
-server.py
-test_pb2.py
-test_pb2_grpc.py
run docker build .
in the directory “somename”. This should create a docker image. Copy the created image ID image-id
.
Using docker run -it image-id
, replace the image-id
with your image ID that was created by docker build. You need to do this twice in two different command line so that you have two different container. Find the two container ID. In the following code, I use container1
and container2
to identify two different containers id.
The next step is to link this two containers via an network. We can use docker network
command to achieve this.
open a third terminal and type:
docker network create testnet
docker network connect testnet container1 --alias client
docker network connect testnet container2 --alias server
If you have gone through the code carefully, you may wondering where was the ‘‘server:22222" come from in client.py
code. Well, here is it. The fact is we name the container2 using the network alias as “server” so all the containers in this network can use “server” to find this container. You can also use the container ID to replace “server”.
So now, you can run the command python3 app/server.py
on the container2 and run python3 app/client.py
on the container1. You should be able to see the client side successfully get the encoded message from server.
WELL DONE! Now, we have realized the comunication between two containers with grpc!
That’s all about this post. Let me know if you have any questions.