Introduction to Docker
Docker container를 GCP에서 run, build, debug하는지에 관한 실습. 이 실습에서는
- docker hub / Google container registry에 어떻게 docker 이미지를 풀하는지
- GCR 에서부터 docker images를 어떻게 push받는지
를 실습했다.
- Dockerfile 만들기
Dockerfile을 만드는데 스펙을 다음과 같이 만듦
cat > Dockerfile << EOF
# base parent이미지를 명시한다 (이 경우에는 node version 6라고 명시함)
FROM node:6
# Set the working directory in the container to /app (container실행경로가 /app으로 하도록 작업경로 설정)
WORKDIR /app
# Copy the current directory contents into the container at /app
ADD . /app (현재 작업경로의 컨텐츠를 container에 add함)
# Make the container's port 80 available to the outside world
EXPOSE 80 (container 포트 노출)
# Run app.js using node when the container launches
CMD ["node", "app.js"]
EOF
- Node application 만들기
80번 포트로 접근할 수 있는 hello world 출력하는 간단 앱을 만든다.
cat > app.js <<EOF
const http = require('http');
const hostname = '0.0.0.0';
const port = 80;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});
server.listen(port, hostname, () => {
console.log('Server running at http://%s:%s/', hostname, port);
});
process.on('SIGINT', function() {
console.log('Caught interrupt signal and will exit');
process.exit();
});
EOF
docker images 명령어로 확인해보면 0.1버전으로 도커파일이 명시되어 있다
student_04_13d47ca1e9b3@cloudshell:~/test (qwiklabs-gcp-00-6a9fb388c3c3)$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
node-app 0.1 4da28894f75a 39 seconds ago 884MB
hello-world latest d1165f221234 6 months ago 13.3kB
node 6 ab290b853066 2 years ago 884MB
다른 터미널을 열고, curl 로 요청했을 때의 결과
student_04_13d47ca1e9b3@cloudshell:~ (qwiklabs-gcp-00-6a9fb388c3c3)$ curl http://localhost:4000
Hello World
docker bash에 접근하고 싶을 때는? (아까 /app 디렉토리에서 컨테이너를 실행한다고 명시했기 때문에 이 경로로 접속되는 걸 확인할 수 있음)
student_04_13d47ca1e9b3@cloudshell:~/test (qwiklabs-gcp-00-6a9fb388c3c3)$ docker exec -it e9c73fffe77f bash
- 이 이미지를 GCR(Google Container Registry) 로 Publish하기
이미지를 GCR에게 호스팅되는 private registry로 push하려면, 이미지 registry name과 함께 태깅을 꼭 해야한다. The format is [hostname]/[project-id]/[image]:[tag].
Tag와 이어서 gcr에 push까지 하는 cmd는 다음과 같다.
student_04_13d47ca1e9b3@cloudshell:~/test (qwiklabs-gcp-00-6a9fb388c3c3)$ docker tag node-app:0.2 gcr.io/qwiklabs-gcp-00-6a9fb388c3c3/node-app:0.2
student_04_13d47ca1e9b3@cloudshell:~/test (qwiklabs-gcp-00-6a9fb388c3c3)$ docker push gcr.io/qwiklabs-gcp-00-6a9fb388c3c3/node-app:0.2
The push refers to repository [gcr.io/qwiklabs-gcp-00-6a9fb388c3c3/node-app]
cf85c86000d0: Pushed
8c8e6d8018ef: Pushed
f39151891503: Pushed
f1965d3c206f: Pushed
a27518e43e49: Layer already exists
910d7fd9e23e: Layer already exists
4230ff7f2288: Layer already exists
2c719774c1e1: Layer already exists
ec62f19bb3aa: Layer already exists
f94641f1fe1f: Layer already exists
기존 존재하던 docker를 모두 제거해준 후 GCR에서 image를 pull해본다
student_04_13d47ca1e9b3@cloudshell:~/test (qwiklabs-gcp-00-6a9fb388c3c3)$ docker pull gcr.io/qwiklabs-gcp-00-6a9fb388c3c3/node-app:0.2
0.2: Pulling from qwiklabs-gcp-00-6a9fb388c3c3/node-app
c5e155d5a1d1: Pull complete
221d80d00ae9: Pull complete
4250b3117dca: Pull complete
3b7ca19181b2: Pull complete
425d7b2a5bcc: Pull complete
69df12c70287: Pull complete
ea2f5386a42d: Pull complete
d421d2b3c5eb: Pull complete
3d3f5cc96fd9: Pull complete
dfc21c45a13c: Pull complete
Digest: sha256:928d65b1be2111de18666af5e150cdd94bf62d631f4b8881928cb768f96d59c1
Status: Downloaded newer image for gcr.io/qwiklabs-gcp-00-6a9fb388c3c3/node-app:0.2
gcr.io/qwiklabs-gcp-00-6a9fb388c3c3/node-app:0.2
pull한 이미지를 해당 포트로 실행하면
student_04_13d47ca1e9b3@cloudshell:~ (qwiklabs-gcp-00-6a9fb388c3c3)$ curl http://localhost:4000
Jiyoon
야호! GCR에서 이미지 땡겨와서 컨테이너 실행하기 quest 완성
Kubernetes Engine: Qwik Start / GCP에서 Kubernetes Engine Cluster 구축해보기
다음 퀘스트는 GKE에서 클러스터를 orchestration하기 위한 kubernetes engine cluster를 구축한다.
Shell을 이용해 GKE Cluster를 구축해본다.
클러스터 creating 전에 먼저 default compute zone을 설정하는데
gcloud config set compute/zone us-central1-a
로 us-central / Zone A로 설정했다.
student_00_6b130b2897a7@cloudshell:~ (qwiklabs-gcp-04-ba9754128013)$ gcloud container clusters create jiyoon-cluster
WARNING: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s).
WARNING: Starting with version 1.19, newly created clusters and node-pools will have COS_CONTAINERD as the default node image when no image type is specified.
Creating cluster jiyoon-cluster in us-central1-a...done.
Created [https://container.googleapis.com/v1/projects/qwiklabs-gcp-01-500142021c34/zones/us-central1-a/clusters/jiyoon-cluster].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-a/jiyoon-cluster?project=qwiklabs-gcp-01-500142021c34
kubeconfig entry generated for jiyoon-cluster.
NAME: jiyoon-cluster
LOCATION: us-central1-a
MASTER_VERSION: 1.20.9-gke.701
MASTER_IP: 104.154.182.127
MACHINE_TYPE: e2-medium
NODE_VERSION: 1.20.9-gke.701
NUM_NODES: 3
STATUS: RUNNING
- 클러스터 Access auth credentials 받기
gcloud container clusters get-credentials [CLUSTER-NAME]
- 내 클러스터에 App deploy하기
You can now deploy a containerized application to the cluster. For this lab, you'll run hello-app in your cluster.
student_00_df37a4b73fa7@cloudshell:~ (qwiklabs-gcp-01-500142021c34)$ kubectl create deployment hello-server --image=gcr.io/google-samples/hello-app:1.0
deployment.apps/hello-server created
To create a Kubernetes Service, which is a Kubernetes resource that lets you expose your application to external traffic, run the following kubectl expose command:
내 app을 external traffic으로 노출시키기 위해, K8S 오브젝트인 service를 생성한다.
student_00_df37a4b73fa7@cloudshell:~ (qwiklabs-gcp-01-500142021c34)$ kubectl expose deployment hello-server --type=LoadBalancer --port 8080
service/hello-server exposed
kubectl cmd로 서비스에 대한 정보를 얻는다.
student_00_df37a4b73fa7@cloudshell:~ (qwiklabs-gcp-01-500142021c34)$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-server LoadBalancer 10.7.254.122 34.133.67.214 8080:32531/TCP 53s
kubernetes ClusterIP 10.7.240.1 <none> 443/TCP 7m20s
그리고 나와있는 external ip를 통해 cluster의 외부인 브라우저에서 접근해보면
꺅!
뭐.. 클러스터 삭제를 왜 하는지는 모르겠지만.. 현실에선 이럴일은 거의 없겠지만.. 그래도 실습에서 delete까지 해보면
student_00_df37a4b73fa7@cloudshell:~ (qwiklabs-gcp-01-500142021c34)$ gcloud container clusters delete jiyoon-cluster
The following clusters will be deleted.
- [jiyoon-cluster] in [us-central1-a]
Do you want to continue (Y/n)? y
Deleting cluster jiyoon-cluster...working.
클러스터 삭제까지 실습 완료.
note) 클러스터 생성과 삭제는 provisioning에 시간이 좀 걸리는 경향이 있다. 아니 겁나 오래걸림 한 5분 이상?
Orchestrating the Cloud with Kubernetes (쿠버네티스를 cloud 환경에서 orchestration하기)
이 랩에서는 Kubernetes engine을 이용해서 kubernetes cluster를 provisioning하고, kubectl
을 이용해서 docker containers를 managing 한 후, kubernetes deployment와 service를 이용해서 app을 microservice로 쪼개는 실습을 해봤다.
-
Google K8S Engine default region 세팅하기
gcloud config set compute/zone us-central1-b
그리고 io라는 이름의 클러스터를 시작해준다.
student_00_f2561c75d8fe@cloudshell:~ (qwiklabs-gcp-03-b5e575611d87)$ gcloud container clusters create io
이번 실습에 필요한 몇가지 설정파일들을 github repo에 공개해 놓았는데, 이 Gitrepo에서 파일들을 clone해온다.
-
Pod creation
Pod에 대한 내용은 간략하게는.. kubernetes 의 core로써 하나 혹은 그 이상의 컨테이너 collection이다. 배포의 단위가 되며, 수명주기 관리의 단위가 된다.
container 사이에 강력한 dependency가 있는 경우에 single pod로 묶어서 배포한다.
apiVersion: v1 kind: Pod metadata: name: monolith labels: app: monolith spec: containers: - name: monolith image: kelseyhightower/monolith:1.0.0 args: - "-http=0.0.0.0:80" - "-health=0.0.0.0:81" - "-secret=secret" ports: - name: http containerPort: 80 - name: health containerPort: 81 resources: limits: cpu: 0.2 memory: "10Mi"
이 yaml파일에서 세가지 사실을 확인할 수 있다.
- monolith라는 하나의 컨테이너로 이루어져 있음
- container가 시작할 때 몇가지 args를 전달한다.
- 80번포트를 열어 http traffic을 컨트롤한다.
이 yaml파일을 가지고 kuber cluster의 resource로 생성하는 cmd는 다음과 같다.
`kubectl create -f pods/monolith.yaml`
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ kubectl get pods
NAME READY STATUS RESTARTS AGE
monolith 1/1 Running 0 11s
nginx-56cd7f6b6-w7rfx 1/1 Running 0 5m37s
```
1. Pod와 interacting하기
기본적으로 pod는 private ip address로 생성되기 때문에 kubectl port-forward command를 이용해서 local port를 cluster 내부의 port와 일치시켜야한다.
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ kubectl port-forward monolith 10080:80
Forwarding from 127.0.0.1:10080 -> 80
```
설정되지 않은 엉뚱한 secure endpoint에 curl을 요청해보았더니
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ curl http://127.0.0.1:10080/secure
authorization failed
```
역시나 권한 failed
curl -u user [http://127.0.0.1:10080/login으](http://127.0.0.1:10080/login으로)로 접근해봤는데 super-secret password인 "password"로 로그인했고,
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ curl -u user http://127.0.0.1:10080/login
Enter host password for user 'user':
{"token":"eyJh이하생략"}
```
토큰을 발급해주며,
이제 우리는 이걸 ENV로 만들어본다. 이 환경변수에 저장된 토큰을 가지고 /secure url에 다시 접근해볼꺼다
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:10080/secure
{"message":"Hello"}
```
나도 hello~
1. container bash 직접 접근하고 컨테이너 안에서 괜히 한번 ping test해보기
```yaml
kubectl exec monolith --stdin --tty -c monolith /bin/sh
tudent_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ kubectl exec monolith --stdin --tty -c monolith /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/
/ # ping -c 3 google.com
PING google.com (172.253.119.101): 56 data bytes
64 bytes from 172.253.119.101: seq=0 ttl=114 time=0.924 ms
64 bytes from 172.253.119.101: seq=1 ttl=114 time=0.860 ms
64 bytes from 172.253.119.101: seq=2 ttl=114 time=0.972 ms
```
1. Service creation
Pod는 persistant한 자원이 아니다. 바로 liveness / rediness 체크를 통해 provisioning에 실패할 시 바로 재 creation하는 stateless한 자원이다.
그래서 일정한 ip address로 pod에 접근할 수 있도록 보장하기 위해 service라는 자원을 만들어서 pod에 지속적으로 붙일 수 있다.
3가지 타입이 있는데,
- ClusterIP : 이 서비스가 cluster 내에서만 visible함
- NodePort : 클러스터의 각 노드가 외부에서 access 가능하도록 한다.
- LoadBalancer : cloud provider에서 제공하는 Load balancer 타입으로, 그 LB안에서 service → Node로 트래픽을 서빙한다.
service를 생성한 이후에는, 노출된 nodeport로 트래픽을 허용하기 위해 firewall-rules를 만들어준다.
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ gcloud compute firewall-rules create allow-monolith-nodeport \
> --allow=tcp:31000
Creating firewall...working..Created [https://www.googleapis.com/compute/v1/projects/qwiklabs-gcp-03-b5e575611d87/global/firewalls/allow-monolith-nodeport].
Creating firewall...done.
NAME: allow-monolith-nodeport
NETWORK: default
DIRECTION: INGRESS
PRIORITY: 1000
ALLOW: tcp:31000
DENY:
DISABLED: False
```
서비스를 노출하고, firewall rules까지 만들어줬는데
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ curl -k https://34.71.149.193:31000
curl: (7) Failed to connect to 34.71.149.193 port 31000: Connection refused
```
external ip의 포트로 접근하면 connection fail이 뜬다. 아니 왜?
왜냐하면 애초에 이 service를 creation할 때, app=monolith라는 label selector를 통해 pod를 선택해 이 pod의 트래픽을 고정시킨건데, 정작 pod에 label이 아직 안달려있기 때문
이제 pod에 라벨을 달아서 service가 pod를 제대로 인식할 수 있게 해주자
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ kubectl label pods secure-monolith 'secure=enabled'
pod/secure-monolith labeled
```
```yaml
student_00_f2561c75d8fe@cloudshell:~/orchestrate-with-kubernetes/kubernetes (qwiklabs-gcp-03-b5e575611d87)$ curl -k https://34.71.149.193:31000
{"message":"Hello"}
```
음 잘되는군 만족!
Deployment
배포의 2종류
**Rolling Update**
New replicaset을 만들고, new replicaset의 숫자를 천천히 증가하는 동시에 old replicaset의 숫자를 천천히 감소시킨다.
최소의 overhead, 최소의 perfomance 영향도, 최소의 downtime으로 업데이트할수 있다는 부분이 benefical.
![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/50bb6b67-ab7b-401f-b377-bde24530c316/Untitled.png)
**Canary Update**
완전히 seperated된 deployment를 만들고, 서비스의 타겟을 new deployment로 어느 순간 바꾼다
![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2ea90347-ca48-4b71-80e9-5788fecc0391/Untitled.png)
Blue-Green deployments
쿠버네티스가 Rolling update를 수행하는 방식 / old에 blue version, new 에 green version의 태그를 단다.
## Note) Understanding Regions and Zones in 구글클라우드 (구글클라우드에서의 region / zone 개념)
Certain Compute Engine resources live in regions or zones. A region is a specific geographical location where you can run your resources. Each region has one or more zones. For example, the us-central1 region denotes a region in the Central United States that has zones `us-central1-a`, `us-central1-b`, `us-central1-c`, and `us-central1-f`.
![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d3f2e073-92bd-4db7-906f-3e8bf68087a6/Untitled.png)
Resources that live in a zone are referred to as zonal resources. Virtual machine Instances and persistent disks live in a zone. To attach a persistent disk to a virtual machine instance, both resources must be in the same zone. Similarly, if you want to assign a static IP address to an instance, the instance must be in the same region as the static IP.
Top comments (0)